133190b70SSascha Wildner /* 233190b70SSascha Wildner * Copyright (c) 2010, LSI Corp. 333190b70SSascha Wildner * All rights reserved. 433190b70SSascha Wildner * Author : Manjunath Ranganathaiah 533190b70SSascha Wildner * Support: freebsdraid@lsi.com 633190b70SSascha Wildner * 733190b70SSascha Wildner * Redistribution and use in source and binary forms, with or without 833190b70SSascha Wildner * modification, are permitted provided that the following conditions 933190b70SSascha Wildner * are met: 1033190b70SSascha Wildner * 1133190b70SSascha Wildner * 1. Redistributions of source code must retain the above copyright 1233190b70SSascha Wildner * notice, this list of conditions and the following disclaimer. 1333190b70SSascha Wildner * 2. Redistributions in binary form must reproduce the above copyright 1433190b70SSascha Wildner * notice, this list of conditions and the following disclaimer in 1533190b70SSascha Wildner * the documentation and/or other materials provided with the 1633190b70SSascha Wildner * distribution. 1733190b70SSascha Wildner * 3. Neither the name of the <ORGANIZATION> nor the names of its 1833190b70SSascha Wildner * contributors may be used to endorse or promote products derived 1933190b70SSascha Wildner * from this software without specific prior written permission. 2033190b70SSascha Wildner * 2133190b70SSascha Wildner * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 2233190b70SSascha Wildner * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 2333190b70SSascha Wildner * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 2433190b70SSascha Wildner * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 2533190b70SSascha Wildner * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 2633190b70SSascha Wildner * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 2733190b70SSascha Wildner * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 2833190b70SSascha Wildner * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 2933190b70SSascha Wildner * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3033190b70SSascha Wildner * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 3133190b70SSascha Wildner * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 3233190b70SSascha Wildner * POSSIBILITY OF SUCH DAMAGE. 3333190b70SSascha Wildner * 3433190b70SSascha Wildner * $FreeBSD: src/sys/dev/tws/tws.c,v 1.3 2007/05/09 04:16:32 mrangana Exp $ 3533190b70SSascha Wildner */ 3633190b70SSascha Wildner 3733190b70SSascha Wildner #include <dev/raid/tws/tws.h> 3833190b70SSascha Wildner #include <dev/raid/tws/tws_services.h> 3933190b70SSascha Wildner #include <dev/raid/tws/tws_hdm.h> 4033190b70SSascha Wildner 4133190b70SSascha Wildner #include <bus/cam/cam.h> 4233190b70SSascha Wildner #include <bus/cam/cam_ccb.h> 4333190b70SSascha Wildner 445ef3096fSSascha Wildner static int tws_msi_enable = 1; 455ef3096fSSascha Wildner 4633190b70SSascha Wildner MALLOC_DEFINE(M_TWS, "twsbuf", "buffers used by tws driver"); 4733190b70SSascha Wildner int tws_queue_depth = TWS_MAX_REQS; 4833190b70SSascha Wildner 4933190b70SSascha Wildner /* externs */ 5033190b70SSascha Wildner extern int tws_cam_attach(struct tws_softc *sc); 5133190b70SSascha Wildner extern void tws_cam_detach(struct tws_softc *sc); 5233190b70SSascha Wildner extern int tws_init_ctlr(struct tws_softc *sc); 5333190b70SSascha Wildner extern boolean tws_ctlr_ready(struct tws_softc *sc); 5433190b70SSascha Wildner extern void tws_turn_off_interrupts(struct tws_softc *sc); 5533190b70SSascha Wildner extern void tws_q_insert_tail(struct tws_softc *sc, struct tws_request *req, 5633190b70SSascha Wildner u_int8_t q_type ); 5733190b70SSascha Wildner extern struct tws_request *tws_q_remove_request(struct tws_softc *sc, 5833190b70SSascha Wildner struct tws_request *req, u_int8_t q_type ); 5933190b70SSascha Wildner extern struct tws_request *tws_q_remove_head(struct tws_softc *sc, 6033190b70SSascha Wildner u_int8_t q_type ); 6133190b70SSascha Wildner extern boolean tws_get_response(struct tws_softc *sc, u_int16_t *req_id); 6233190b70SSascha Wildner extern boolean tws_ctlr_reset(struct tws_softc *sc); 6333190b70SSascha Wildner extern void tws_intr(void *arg); 6433190b70SSascha Wildner extern int tws_use_32bit_sgls; 6533190b70SSascha Wildner 6633190b70SSascha Wildner 6733190b70SSascha Wildner struct tws_request *tws_get_request(struct tws_softc *sc, u_int16_t type); 6833190b70SSascha Wildner int tws_init_connect(struct tws_softc *sc, u_int16_t mc); 6933190b70SSascha Wildner void tws_send_event(struct tws_softc *sc, u_int8_t event); 7033190b70SSascha Wildner uint8_t tws_get_state(struct tws_softc *sc); 7133190b70SSascha Wildner void tws_release_request(struct tws_request *req); 7233190b70SSascha Wildner 7333190b70SSascha Wildner 7433190b70SSascha Wildner 7533190b70SSascha Wildner /* Function prototypes */ 7633190b70SSascha Wildner static d_open_t tws_open; 7733190b70SSascha Wildner static d_close_t tws_close; 7833190b70SSascha Wildner static d_read_t tws_read; 7933190b70SSascha Wildner static d_write_t tws_write; 8033190b70SSascha Wildner extern d_ioctl_t tws_ioctl; 8133190b70SSascha Wildner 8233190b70SSascha Wildner static int tws_init(struct tws_softc *sc); 8333190b70SSascha Wildner static void tws_dmamap_cmds_load_cbfn(void *arg, bus_dma_segment_t *segs, 8433190b70SSascha Wildner int nseg, int error); 8533190b70SSascha Wildner 8633190b70SSascha Wildner static int tws_init_reqs(struct tws_softc *sc, u_int32_t dma_mem_size); 8733190b70SSascha Wildner static int tws_init_aen_q(struct tws_softc *sc); 8833190b70SSascha Wildner static int tws_init_trace_q(struct tws_softc *sc); 8933190b70SSascha Wildner static int tws_setup_irq(struct tws_softc *sc); 9033190b70SSascha Wildner 9133190b70SSascha Wildner 9233190b70SSascha Wildner /* Character device entry points */ 9333190b70SSascha Wildner 9433190b70SSascha Wildner static struct dev_ops tws_ops = { 9533190b70SSascha Wildner { "tws", 0, 0 }, 9633190b70SSascha Wildner .d_open = tws_open, 9733190b70SSascha Wildner .d_close = tws_close, 9833190b70SSascha Wildner .d_read = tws_read, 9933190b70SSascha Wildner .d_write = tws_write, 10033190b70SSascha Wildner .d_ioctl = tws_ioctl, 10133190b70SSascha Wildner }; 10233190b70SSascha Wildner 10333190b70SSascha Wildner /* 10433190b70SSascha Wildner * In the cdevsw routines, we find our softc by using the si_drv1 member 10533190b70SSascha Wildner * of struct cdev. We set this variable to point to our softc in our 10633190b70SSascha Wildner * attach routine when we create the /dev entry. 10733190b70SSascha Wildner */ 10833190b70SSascha Wildner 109*8406cf70SSascha Wildner static int 11033190b70SSascha Wildner tws_open(struct dev_open_args *ap) 11133190b70SSascha Wildner { 11233190b70SSascha Wildner cdev_t dev = ap->a_head.a_dev; 11333190b70SSascha Wildner struct tws_softc *sc = dev->si_drv1; 11433190b70SSascha Wildner 11533190b70SSascha Wildner if ( sc ) 11633190b70SSascha Wildner TWS_TRACE_DEBUG(sc, "entry", dev, oflags); 11733190b70SSascha Wildner return (0); 11833190b70SSascha Wildner } 11933190b70SSascha Wildner 120*8406cf70SSascha Wildner static int 12133190b70SSascha Wildner tws_close(struct dev_close_args *ap) 12233190b70SSascha Wildner { 12333190b70SSascha Wildner cdev_t dev = ap->a_head.a_dev; 12433190b70SSascha Wildner struct tws_softc *sc = dev->si_drv1; 12533190b70SSascha Wildner 12633190b70SSascha Wildner if ( sc ) 12733190b70SSascha Wildner TWS_TRACE_DEBUG(sc, "entry", dev, fflag); 12833190b70SSascha Wildner return (0); 12933190b70SSascha Wildner } 13033190b70SSascha Wildner 131*8406cf70SSascha Wildner static int 13233190b70SSascha Wildner tws_read(struct dev_read_args *ap) 13333190b70SSascha Wildner { 13433190b70SSascha Wildner cdev_t dev = ap->a_head.a_dev; 13533190b70SSascha Wildner struct tws_softc *sc = dev->si_drv1; 13633190b70SSascha Wildner 13733190b70SSascha Wildner if ( sc ) 13833190b70SSascha Wildner TWS_TRACE_DEBUG(sc, "entry", dev, ioflag); 13933190b70SSascha Wildner return (0); 14033190b70SSascha Wildner } 14133190b70SSascha Wildner 142*8406cf70SSascha Wildner static int 14333190b70SSascha Wildner tws_write(struct dev_write_args *ap) 14433190b70SSascha Wildner { 14533190b70SSascha Wildner cdev_t dev = ap->a_head.a_dev; 14633190b70SSascha Wildner struct tws_softc *sc = dev->si_drv1; 14733190b70SSascha Wildner 14833190b70SSascha Wildner if ( sc ) 14933190b70SSascha Wildner TWS_TRACE_DEBUG(sc, "entry", dev, ioflag); 15033190b70SSascha Wildner return (0); 15133190b70SSascha Wildner } 15233190b70SSascha Wildner 15333190b70SSascha Wildner /* PCI Support Functions */ 15433190b70SSascha Wildner 15533190b70SSascha Wildner /* 15633190b70SSascha Wildner * Compare the device ID of this device against the IDs that this driver 15733190b70SSascha Wildner * supports. If there is a match, set the description and return success. 15833190b70SSascha Wildner */ 15933190b70SSascha Wildner static int 16033190b70SSascha Wildner tws_probe(device_t dev) 16133190b70SSascha Wildner { 16233190b70SSascha Wildner static u_int8_t first_ctlr = 1; 16333190b70SSascha Wildner 16433190b70SSascha Wildner if ((pci_get_vendor(dev) == TWS_VENDOR_ID) && 16533190b70SSascha Wildner (pci_get_device(dev) == TWS_DEVICE_ID)) { 16633190b70SSascha Wildner device_set_desc(dev, "LSI 3ware SAS/SATA Storage Controller"); 16733190b70SSascha Wildner if (first_ctlr) { 16833190b70SSascha Wildner kprintf("LSI 3ware device driver for SAS/SATA storage " 16933190b70SSascha Wildner "controllers, version: %s\n", TWS_DRIVER_VERSION_STRING); 17033190b70SSascha Wildner first_ctlr = 0; 17133190b70SSascha Wildner } 17233190b70SSascha Wildner 17333190b70SSascha Wildner return(0); 17433190b70SSascha Wildner } 17533190b70SSascha Wildner return (ENXIO); 17633190b70SSascha Wildner } 17733190b70SSascha Wildner 17833190b70SSascha Wildner /* Attach function is only called if the probe is successful. */ 17933190b70SSascha Wildner 18033190b70SSascha Wildner static int 18133190b70SSascha Wildner tws_attach(device_t dev) 18233190b70SSascha Wildner { 18333190b70SSascha Wildner struct tws_softc *sc = device_get_softc(dev); 18433190b70SSascha Wildner u_int32_t cmd, bar; 1855ef3096fSSascha Wildner int error=0; 18633190b70SSascha Wildner 18733190b70SSascha Wildner /* no tracing yet */ 18833190b70SSascha Wildner /* Look up our softc and initialize its fields. */ 18933190b70SSascha Wildner sc->tws_dev = dev; 19033190b70SSascha Wildner sc->device_id = pci_get_device(dev); 19133190b70SSascha Wildner sc->subvendor_id = pci_get_subvendor(dev); 19233190b70SSascha Wildner sc->subdevice_id = pci_get_subdevice(dev); 19333190b70SSascha Wildner 19433190b70SSascha Wildner /* Intialize mutexes */ 19533190b70SSascha Wildner lockinit(&sc->q_lock, "tws_q_lock", 0, LK_CANRECURSE); 19633190b70SSascha Wildner lockinit(&sc->sim_lock, "tws_sim_lock", 0, LK_CANRECURSE); 19733190b70SSascha Wildner lockinit(&sc->gen_lock, "tws_gen_lock", 0, LK_CANRECURSE); 19833190b70SSascha Wildner lockinit(&sc->io_lock, "tws_io_lock", 0, LK_CANRECURSE); 19933190b70SSascha Wildner 20033190b70SSascha Wildner if ( tws_init_trace_q(sc) == FAILURE ) 20133190b70SSascha Wildner kprintf("trace init failure\n"); 20233190b70SSascha Wildner /* send init event */ 20333190b70SSascha Wildner lockmgr(&sc->gen_lock, LK_EXCLUSIVE); 20433190b70SSascha Wildner tws_send_event(sc, TWS_INIT_START); 20533190b70SSascha Wildner lockmgr(&sc->gen_lock, LK_RELEASE); 20633190b70SSascha Wildner 20733190b70SSascha Wildner 20833190b70SSascha Wildner #if _BYTE_ORDER == _BIG_ENDIAN 20933190b70SSascha Wildner TWS_TRACE(sc, "BIG endian", 0, 0); 21033190b70SSascha Wildner #endif 21126595b18SSascha Wildner SYSCTL_ADD_STRING(device_get_sysctl_ctx(dev), 21226595b18SSascha Wildner SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 21333190b70SSascha Wildner OID_AUTO, "driver_version", CTLFLAG_RD, 21433190b70SSascha Wildner TWS_DRIVER_VERSION_STRING, 0, "TWS driver version"); 21533190b70SSascha Wildner 21633190b70SSascha Wildner cmd = pci_read_config(dev, PCIR_COMMAND, 2); 21733190b70SSascha Wildner if ( (cmd & PCIM_CMD_PORTEN) == 0) { 21833190b70SSascha Wildner tws_log(sc, PCI_COMMAND_READ); 21933190b70SSascha Wildner goto attach_fail_1; 22033190b70SSascha Wildner } 22133190b70SSascha Wildner /* Force the busmaster enable bit on. */ 22233190b70SSascha Wildner cmd |= PCIM_CMD_BUSMASTEREN; 22333190b70SSascha Wildner pci_write_config(dev, PCIR_COMMAND, cmd, 2); 22433190b70SSascha Wildner 22533190b70SSascha Wildner bar = pci_read_config(dev, TWS_PCI_BAR0, 4); 22633190b70SSascha Wildner TWS_TRACE_DEBUG(sc, "bar0 ", bar, 0); 22733190b70SSascha Wildner bar = pci_read_config(dev, TWS_PCI_BAR1, 4); 22833190b70SSascha Wildner bar = bar & ~TWS_BIT2; 22933190b70SSascha Wildner TWS_TRACE_DEBUG(sc, "bar1 ", bar, 0); 23033190b70SSascha Wildner 23133190b70SSascha Wildner /* MFA base address is BAR2 register used for 23233190b70SSascha Wildner * push mode. Firmware will evatualy move to 23333190b70SSascha Wildner * pull mode during witch this needs to change 23433190b70SSascha Wildner */ 23533190b70SSascha Wildner #ifndef TWS_PULL_MODE_ENABLE 23633190b70SSascha Wildner sc->mfa_base = (u_int64_t)pci_read_config(dev, TWS_PCI_BAR2, 4); 23733190b70SSascha Wildner sc->mfa_base = sc->mfa_base & ~TWS_BIT2; 23833190b70SSascha Wildner TWS_TRACE_DEBUG(sc, "bar2 ", sc->mfa_base, 0); 23933190b70SSascha Wildner #endif 24033190b70SSascha Wildner 24133190b70SSascha Wildner /* allocate MMIO register space */ 24233190b70SSascha Wildner sc->reg_res_id = TWS_PCI_BAR1; /* BAR1 offset */ 24333190b70SSascha Wildner if ((sc->reg_res = bus_alloc_resource(dev, SYS_RES_MEMORY, 24433190b70SSascha Wildner &(sc->reg_res_id), 0, ~0, 1, RF_ACTIVE)) 24533190b70SSascha Wildner == NULL) { 24633190b70SSascha Wildner tws_log(sc, ALLOC_MEMORY_RES); 24733190b70SSascha Wildner goto attach_fail_1; 24833190b70SSascha Wildner } 24933190b70SSascha Wildner sc->bus_tag = rman_get_bustag(sc->reg_res); 25033190b70SSascha Wildner sc->bus_handle = rman_get_bushandle(sc->reg_res); 25133190b70SSascha Wildner 25233190b70SSascha Wildner #ifndef TWS_PULL_MODE_ENABLE 25333190b70SSascha Wildner /* Allocate bus space for inbound mfa */ 25433190b70SSascha Wildner sc->mfa_res_id = TWS_PCI_BAR2; /* BAR2 offset */ 25533190b70SSascha Wildner if ((sc->mfa_res = bus_alloc_resource(dev, SYS_RES_MEMORY, 25633190b70SSascha Wildner &(sc->mfa_res_id), 0, ~0, 0x100000, RF_ACTIVE)) 25733190b70SSascha Wildner == NULL) { 25833190b70SSascha Wildner tws_log(sc, ALLOC_MEMORY_RES); 25933190b70SSascha Wildner goto attach_fail_2; 26033190b70SSascha Wildner } 26133190b70SSascha Wildner sc->bus_mfa_tag = rman_get_bustag(sc->mfa_res); 26233190b70SSascha Wildner sc->bus_mfa_handle = rman_get_bushandle(sc->mfa_res); 26333190b70SSascha Wildner #endif 26433190b70SSascha Wildner 26533190b70SSascha Wildner /* Allocate and register our interrupt. */ 26633190b70SSascha Wildner if ( tws_setup_irq(sc) == FAILURE ) { 26733190b70SSascha Wildner tws_log(sc, ALLOC_MEMORY_RES); 26833190b70SSascha Wildner goto attach_fail_3; 26933190b70SSascha Wildner } 27033190b70SSascha Wildner 27133190b70SSascha Wildner /* Init callouts. */ 27233190b70SSascha Wildner callout_init(&sc->print_stats_handle); 27333190b70SSascha Wildner callout_init(&sc->reset_cb_handle); 27433190b70SSascha Wildner callout_init(&sc->reinit_handle); 27533190b70SSascha Wildner 27633190b70SSascha Wildner /* 27733190b70SSascha Wildner * Create a /dev entry for this device. The kernel will assign us 27833190b70SSascha Wildner * a major number automatically. We use the unit number of this 27933190b70SSascha Wildner * device as the minor number and name the character device 28033190b70SSascha Wildner * "tws<unit>". 28133190b70SSascha Wildner */ 28233190b70SSascha Wildner sc->tws_cdev = make_dev(&tws_ops, device_get_unit(dev), 28333190b70SSascha Wildner UID_ROOT, GID_OPERATOR, S_IRUSR | S_IWUSR, "tws%u", 28433190b70SSascha Wildner device_get_unit(dev)); 28533190b70SSascha Wildner sc->tws_cdev->si_drv1 = sc; 28633190b70SSascha Wildner 28733190b70SSascha Wildner if ( tws_init(sc) == FAILURE ) { 28833190b70SSascha Wildner tws_log(sc, TWS_INIT_FAILURE); 28933190b70SSascha Wildner goto attach_fail_4; 29033190b70SSascha Wildner } 29133190b70SSascha Wildner if ( tws_init_ctlr(sc) == FAILURE ) { 29233190b70SSascha Wildner tws_log(sc, TWS_CTLR_INIT_FAILURE); 29333190b70SSascha Wildner goto attach_fail_4; 29433190b70SSascha Wildner } 29533190b70SSascha Wildner if ((error = tws_cam_attach(sc))) { 29633190b70SSascha Wildner tws_log(sc, TWS_CAM_ATTACH); 29733190b70SSascha Wildner goto attach_fail_4; 29833190b70SSascha Wildner } 29933190b70SSascha Wildner /* send init complete event */ 30033190b70SSascha Wildner lockmgr(&sc->gen_lock, LK_EXCLUSIVE); 30133190b70SSascha Wildner tws_send_event(sc, TWS_INIT_COMPLETE); 30233190b70SSascha Wildner lockmgr(&sc->gen_lock, LK_RELEASE); 30333190b70SSascha Wildner 30433190b70SSascha Wildner TWS_TRACE_DEBUG(sc, "attached successfully", 0, sc->device_id); 30533190b70SSascha Wildner return(0); 30633190b70SSascha Wildner 30733190b70SSascha Wildner attach_fail_4: 3085ef3096fSSascha Wildner if (sc->intr_handle) { 30933190b70SSascha Wildner if ((error = bus_teardown_intr(sc->tws_dev, 3105ef3096fSSascha Wildner sc->irq_res, sc->intr_handle))) 31133190b70SSascha Wildner TWS_TRACE(sc, "bus teardown intr", 0, error); 31233190b70SSascha Wildner } 31333190b70SSascha Wildner destroy_dev(sc->tws_cdev); 31433190b70SSascha Wildner dev_ops_remove_minor(&tws_ops, device_get_unit(sc->tws_dev)); 31533190b70SSascha Wildner attach_fail_3: 3165ef3096fSSascha Wildner if (sc->irq_res) { 31733190b70SSascha Wildner if (bus_release_resource(sc->tws_dev, 3185ef3096fSSascha Wildner SYS_RES_IRQ, sc->irq_res_id, sc->irq_res)) 31933190b70SSascha Wildner TWS_TRACE(sc, "bus irq res", 0, 0); 32033190b70SSascha Wildner } 32133190b70SSascha Wildner #ifndef TWS_PULL_MODE_ENABLE 32233190b70SSascha Wildner attach_fail_2: 32333190b70SSascha Wildner #endif 32433190b70SSascha Wildner if ( sc->mfa_res ){ 32533190b70SSascha Wildner if (bus_release_resource(sc->tws_dev, 32633190b70SSascha Wildner SYS_RES_MEMORY, sc->mfa_res_id, sc->mfa_res)) 32733190b70SSascha Wildner TWS_TRACE(sc, "bus release ", 0, sc->mfa_res_id); 32833190b70SSascha Wildner } 32933190b70SSascha Wildner if ( sc->reg_res ){ 33033190b70SSascha Wildner if (bus_release_resource(sc->tws_dev, 33133190b70SSascha Wildner SYS_RES_MEMORY, sc->reg_res_id, sc->reg_res)) 33233190b70SSascha Wildner TWS_TRACE(sc, "bus release2 ", 0, sc->reg_res_id); 33333190b70SSascha Wildner } 33433190b70SSascha Wildner attach_fail_1: 33533190b70SSascha Wildner lockuninit(&sc->q_lock); 33633190b70SSascha Wildner lockuninit(&sc->sim_lock); 33733190b70SSascha Wildner lockuninit(&sc->gen_lock); 33833190b70SSascha Wildner lockuninit(&sc->io_lock); 33933190b70SSascha Wildner return (ENXIO); 34033190b70SSascha Wildner } 34133190b70SSascha Wildner 34233190b70SSascha Wildner /* Detach device. */ 34333190b70SSascha Wildner 34433190b70SSascha Wildner static int 34533190b70SSascha Wildner tws_detach(device_t dev) 34633190b70SSascha Wildner { 34733190b70SSascha Wildner struct tws_softc *sc = device_get_softc(dev); 3485ef3096fSSascha Wildner int error; 34933190b70SSascha Wildner u_int32_t reg; 35033190b70SSascha Wildner 35133190b70SSascha Wildner TWS_TRACE_DEBUG(sc, "entry", 0, 0); 35233190b70SSascha Wildner 35333190b70SSascha Wildner lockmgr(&sc->gen_lock, LK_EXCLUSIVE); 35433190b70SSascha Wildner tws_send_event(sc, TWS_UNINIT_START); 35533190b70SSascha Wildner lockmgr(&sc->gen_lock, LK_RELEASE); 35633190b70SSascha Wildner 35733190b70SSascha Wildner /* needs to disable interrupt before detaching from cam */ 35833190b70SSascha Wildner tws_turn_off_interrupts(sc); 35933190b70SSascha Wildner /* clear door bell */ 36033190b70SSascha Wildner tws_write_reg(sc, TWS_I2O0_HOBDBC, ~0, 4); 36133190b70SSascha Wildner reg = tws_read_reg(sc, TWS_I2O0_HIMASK, 4); 36233190b70SSascha Wildner TWS_TRACE_DEBUG(sc, "turn-off-intr", reg, 0); 36333190b70SSascha Wildner sc->obfl_q_overrun = false; 36433190b70SSascha Wildner tws_init_connect(sc, 1); 36533190b70SSascha Wildner 36633190b70SSascha Wildner /* Teardown the state in our softc created in our attach routine. */ 36733190b70SSascha Wildner /* Disconnect the interrupt handler. */ 3685ef3096fSSascha Wildner if (sc->intr_handle) { 36933190b70SSascha Wildner if ((error = bus_teardown_intr(sc->tws_dev, 3705ef3096fSSascha Wildner sc->irq_res, sc->intr_handle))) 37133190b70SSascha Wildner TWS_TRACE(sc, "bus teardown intr", 0, error); 37233190b70SSascha Wildner } 37333190b70SSascha Wildner /* Release irq resource */ 3745ef3096fSSascha Wildner if (sc->irq_res) { 37533190b70SSascha Wildner if (bus_release_resource(sc->tws_dev, 3765ef3096fSSascha Wildner SYS_RES_IRQ, sc->irq_res_id, sc->irq_res)) 3775ef3096fSSascha Wildner TWS_TRACE(sc, "bus release irq resource", 0, sc->irq_res_id); 37833190b70SSascha Wildner } 3795ef3096fSSascha Wildner if (sc->intr_type == PCI_INTR_TYPE_MSI) 38033190b70SSascha Wildner pci_release_msi(sc->tws_dev); 38133190b70SSascha Wildner 38233190b70SSascha Wildner tws_cam_detach(sc); 38333190b70SSascha Wildner 38433190b70SSascha Wildner /* Release memory resource */ 38533190b70SSascha Wildner if ( sc->mfa_res ){ 38633190b70SSascha Wildner if (bus_release_resource(sc->tws_dev, 38733190b70SSascha Wildner SYS_RES_MEMORY, sc->mfa_res_id, sc->mfa_res)) 38833190b70SSascha Wildner TWS_TRACE(sc, "bus release mem resource", 0, sc->mfa_res_id); 38933190b70SSascha Wildner } 39033190b70SSascha Wildner if ( sc->reg_res ){ 39133190b70SSascha Wildner if (bus_release_resource(sc->tws_dev, 39233190b70SSascha Wildner SYS_RES_MEMORY, sc->reg_res_id, sc->reg_res)) 39333190b70SSascha Wildner TWS_TRACE(sc, "bus release mem resource", 0, sc->reg_res_id); 39433190b70SSascha Wildner } 39533190b70SSascha Wildner 39633190b70SSascha Wildner kfree(sc->reqs, M_TWS); 39733190b70SSascha Wildner kfree(sc->sense_bufs, M_TWS); 39833190b70SSascha Wildner kfree(sc->scan_ccb, M_TWS); 39933190b70SSascha Wildner kfree(sc->aen_q.q, M_TWS); 40033190b70SSascha Wildner kfree(sc->trace_q.q, M_TWS); 40133190b70SSascha Wildner lockuninit(&sc->q_lock); 40233190b70SSascha Wildner lockuninit(&sc->sim_lock); 40333190b70SSascha Wildner lockuninit(&sc->gen_lock); 40433190b70SSascha Wildner lockuninit(&sc->io_lock); 40533190b70SSascha Wildner destroy_dev(sc->tws_cdev); 40633190b70SSascha Wildner dev_ops_remove_minor(&tws_ops, device_get_unit(sc->tws_dev)); 40733190b70SSascha Wildner return (0); 40833190b70SSascha Wildner } 40933190b70SSascha Wildner 41033190b70SSascha Wildner static int 41133190b70SSascha Wildner tws_setup_irq(struct tws_softc *sc) 41233190b70SSascha Wildner { 41333190b70SSascha Wildner u_int16_t cmd; 4145ef3096fSSascha Wildner u_int irq_flags; 41533190b70SSascha Wildner 41633190b70SSascha Wildner cmd = pci_read_config(sc->tws_dev, PCIR_COMMAND, 2); 4175ef3096fSSascha Wildner 4185ef3096fSSascha Wildner if (tws_msi_enable) 4195ef3096fSSascha Wildner cmd |= 0x0400; 4205ef3096fSSascha Wildner else 4215ef3096fSSascha Wildner cmd &= ~0x0400; 42233190b70SSascha Wildner pci_write_config(sc->tws_dev, PCIR_COMMAND, cmd, 2); 4235ef3096fSSascha Wildner sc->irq_res = 0; 4245ef3096fSSascha Wildner sc->intr_type = pci_alloc_1intr(sc->tws_dev, tws_msi_enable, 4255ef3096fSSascha Wildner &sc->irq_res_id, &irq_flags); 4265ef3096fSSascha Wildner sc->irq_res = bus_alloc_resource_any(sc->tws_dev, SYS_RES_IRQ, 4275ef3096fSSascha Wildner &sc->irq_res_id, irq_flags); 4285ef3096fSSascha Wildner if (!sc->irq_res) 42933190b70SSascha Wildner return(FAILURE); 4305ef3096fSSascha Wildner if (bus_setup_intr(sc->tws_dev, sc->irq_res, INTR_MPSAFE, tws_intr, sc, 4315ef3096fSSascha Wildner &sc->intr_handle, NULL)) { 4325ef3096fSSascha Wildner tws_log(sc, SETUP_INTR_RES); 43333190b70SSascha Wildner return(FAILURE); 43433190b70SSascha Wildner } 4355ef3096fSSascha Wildner if (sc->intr_type == PCI_INTR_TYPE_MSI) 43633190b70SSascha Wildner device_printf(sc->tws_dev, "Using MSI\n"); 4375ef3096fSSascha Wildner else 4385ef3096fSSascha Wildner device_printf(sc->tws_dev, "Using legacy INTx\n"); 43933190b70SSascha Wildner 44033190b70SSascha Wildner return(SUCCESS); 44133190b70SSascha Wildner } 44233190b70SSascha Wildner 44333190b70SSascha Wildner static int 44433190b70SSascha Wildner tws_init(struct tws_softc *sc) 44533190b70SSascha Wildner { 44633190b70SSascha Wildner 44733190b70SSascha Wildner u_int32_t max_sg_elements; 44833190b70SSascha Wildner u_int32_t dma_mem_size; 44933190b70SSascha Wildner int error; 45033190b70SSascha Wildner u_int32_t reg; 45133190b70SSascha Wildner 45233190b70SSascha Wildner sc->seq_id = 0; 45333190b70SSascha Wildner if ( tws_queue_depth > TWS_MAX_REQS ) 45433190b70SSascha Wildner tws_queue_depth = TWS_MAX_REQS; 45533190b70SSascha Wildner if (tws_queue_depth < TWS_RESERVED_REQS+1) 45633190b70SSascha Wildner tws_queue_depth = TWS_RESERVED_REQS+1; 45733190b70SSascha Wildner sc->is64bit = (sizeof(bus_addr_t) == 8) ? true : false; 45833190b70SSascha Wildner max_sg_elements = (sc->is64bit && !tws_use_32bit_sgls) ? 45933190b70SSascha Wildner TWS_MAX_64BIT_SG_ELEMENTS : 46033190b70SSascha Wildner TWS_MAX_32BIT_SG_ELEMENTS; 46133190b70SSascha Wildner dma_mem_size = (sizeof(struct tws_command_packet) * tws_queue_depth) + 46233190b70SSascha Wildner (TWS_SECTOR_SIZE) ; 46333190b70SSascha Wildner if ( bus_dma_tag_create(NULL, /* parent */ 46433190b70SSascha Wildner TWS_ALIGNMENT, /* alignment */ 46533190b70SSascha Wildner 0, /* boundary */ 46633190b70SSascha Wildner BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 46733190b70SSascha Wildner BUS_SPACE_MAXADDR, /* highaddr */ 46833190b70SSascha Wildner NULL, NULL, /* filter, filterarg */ 46933190b70SSascha Wildner BUS_SPACE_MAXSIZE, /* maxsize */ 47033190b70SSascha Wildner max_sg_elements, /* numsegs */ 47133190b70SSascha Wildner BUS_SPACE_MAXSIZE, /* maxsegsize */ 47233190b70SSascha Wildner 0, /* flags */ 47333190b70SSascha Wildner &sc->parent_tag /* tag */ 47433190b70SSascha Wildner )) { 47533190b70SSascha Wildner TWS_TRACE_DEBUG(sc, "DMA parent tag Create fail", max_sg_elements, 47633190b70SSascha Wildner sc->is64bit); 47733190b70SSascha Wildner return(ENOMEM); 47833190b70SSascha Wildner } 47933190b70SSascha Wildner /* In bound message frame requires 16byte alignment. 48033190b70SSascha Wildner * Outbound MF's can live with 4byte alignment - for now just 48133190b70SSascha Wildner * use 16 for both. 48233190b70SSascha Wildner */ 48333190b70SSascha Wildner if ( bus_dma_tag_create(sc->parent_tag, /* parent */ 48433190b70SSascha Wildner TWS_IN_MF_ALIGNMENT, /* alignment */ 48533190b70SSascha Wildner 0, /* boundary */ 48633190b70SSascha Wildner BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 48733190b70SSascha Wildner BUS_SPACE_MAXADDR, /* highaddr */ 48833190b70SSascha Wildner NULL, NULL, /* filter, filterarg */ 48933190b70SSascha Wildner dma_mem_size, /* maxsize */ 49033190b70SSascha Wildner 1, /* numsegs */ 49133190b70SSascha Wildner BUS_SPACE_MAXSIZE, /* maxsegsize */ 49233190b70SSascha Wildner 0, /* flags */ 49333190b70SSascha Wildner &sc->cmd_tag /* tag */ 49433190b70SSascha Wildner )) { 49533190b70SSascha Wildner TWS_TRACE_DEBUG(sc, "DMA cmd tag Create fail", max_sg_elements, sc->is64bit); 49633190b70SSascha Wildner return(ENOMEM); 49733190b70SSascha Wildner } 49833190b70SSascha Wildner 49933190b70SSascha Wildner if (bus_dmamem_alloc(sc->cmd_tag, &sc->dma_mem, 50033190b70SSascha Wildner BUS_DMA_NOWAIT, &sc->cmd_map)) { 50133190b70SSascha Wildner TWS_TRACE_DEBUG(sc, "DMA mem alloc fail", max_sg_elements, sc->is64bit); 50233190b70SSascha Wildner return(ENOMEM); 50333190b70SSascha Wildner } 50433190b70SSascha Wildner 50533190b70SSascha Wildner /* if bus_dmamem_alloc succeeds then bus_dmamap_load will succeed */ 50633190b70SSascha Wildner sc->dma_mem_phys=0; 50733190b70SSascha Wildner error = bus_dmamap_load(sc->cmd_tag, sc->cmd_map, sc->dma_mem, 50833190b70SSascha Wildner dma_mem_size, tws_dmamap_cmds_load_cbfn, 50933190b70SSascha Wildner &sc->dma_mem_phys, 0); 51033190b70SSascha Wildner 51133190b70SSascha Wildner if ( error == EINPROGRESS ) 51233190b70SSascha Wildner TWS_TRACE_DEBUG(sc, "req queued", max_sg_elements, sc->is64bit); 51333190b70SSascha Wildner 51433190b70SSascha Wildner /* 51533190b70SSascha Wildner * Create a dma tag for data buffers; size will be the maximum 51633190b70SSascha Wildner * possible I/O size (128kB). 51733190b70SSascha Wildner */ 51833190b70SSascha Wildner if (bus_dma_tag_create(sc->parent_tag, /* parent */ 51933190b70SSascha Wildner TWS_ALIGNMENT, /* alignment */ 52033190b70SSascha Wildner 0, /* boundary */ 52133190b70SSascha Wildner BUS_SPACE_MAXADDR_32BIT,/* lowaddr */ 52233190b70SSascha Wildner BUS_SPACE_MAXADDR, /* highaddr */ 52333190b70SSascha Wildner NULL, NULL, /* filter, filterarg */ 52433190b70SSascha Wildner TWS_MAX_IO_SIZE, /* maxsize */ 52533190b70SSascha Wildner max_sg_elements, /* nsegments */ 52633190b70SSascha Wildner TWS_MAX_IO_SIZE, /* maxsegsize */ 52733190b70SSascha Wildner BUS_DMA_ALLOCALL | /* flags */ 52833190b70SSascha Wildner BUS_DMA_ALLOCNOW | 52933190b70SSascha Wildner BUS_DMA_PRIVBZONE | 53033190b70SSascha Wildner BUS_DMA_PROTECTED, 53133190b70SSascha Wildner &sc->data_tag /* tag */)) { 53233190b70SSascha Wildner TWS_TRACE_DEBUG(sc, "DMA cmd tag Create fail", max_sg_elements, sc->is64bit); 53333190b70SSascha Wildner return(ENOMEM); 53433190b70SSascha Wildner } 53533190b70SSascha Wildner 53633190b70SSascha Wildner sc->reqs = kmalloc(sizeof(struct tws_request) * tws_queue_depth, M_TWS, 53733190b70SSascha Wildner M_WAITOK | M_ZERO); 53833190b70SSascha Wildner sc->sense_bufs = kmalloc(sizeof(struct tws_sense) * tws_queue_depth, M_TWS, 53933190b70SSascha Wildner M_WAITOK | M_ZERO); 54033190b70SSascha Wildner sc->scan_ccb = kmalloc(sizeof(union ccb), M_TWS, M_WAITOK | M_ZERO); 54133190b70SSascha Wildner 54233190b70SSascha Wildner if ( !tws_ctlr_ready(sc) ) 54333190b70SSascha Wildner if( !tws_ctlr_reset(sc) ) 54433190b70SSascha Wildner return(FAILURE); 54533190b70SSascha Wildner 54633190b70SSascha Wildner bzero(&sc->stats, sizeof(struct tws_stats)); 54733190b70SSascha Wildner tws_init_qs(sc); 54833190b70SSascha Wildner tws_turn_off_interrupts(sc); 54933190b70SSascha Wildner 55033190b70SSascha Wildner /* 55133190b70SSascha Wildner * enable pull mode by setting bit1 . 55233190b70SSascha Wildner * setting bit0 to 1 will enable interrupt coalesing 55333190b70SSascha Wildner * will revisit. 55433190b70SSascha Wildner */ 55533190b70SSascha Wildner 55633190b70SSascha Wildner #ifdef TWS_PULL_MODE_ENABLE 55733190b70SSascha Wildner 55833190b70SSascha Wildner reg = tws_read_reg(sc, TWS_I2O0_CTL, 4); 55933190b70SSascha Wildner TWS_TRACE_DEBUG(sc, "i20 ctl", reg, TWS_I2O0_CTL); 56033190b70SSascha Wildner tws_write_reg(sc, TWS_I2O0_CTL, reg | TWS_BIT1, 4); 56133190b70SSascha Wildner 56233190b70SSascha Wildner #endif 56333190b70SSascha Wildner 56433190b70SSascha Wildner TWS_TRACE_DEBUG(sc, "dma_mem_phys", sc->dma_mem_phys, TWS_I2O0_CTL); 56533190b70SSascha Wildner if ( tws_init_reqs(sc, dma_mem_size) == FAILURE ) 56633190b70SSascha Wildner return(FAILURE); 56733190b70SSascha Wildner if ( tws_init_aen_q(sc) == FAILURE ) 56833190b70SSascha Wildner return(FAILURE); 56933190b70SSascha Wildner 57033190b70SSascha Wildner return(SUCCESS); 57133190b70SSascha Wildner 57233190b70SSascha Wildner } 57333190b70SSascha Wildner 57433190b70SSascha Wildner static int 57533190b70SSascha Wildner tws_init_aen_q(struct tws_softc *sc) 57633190b70SSascha Wildner { 57733190b70SSascha Wildner sc->aen_q.head=0; 57833190b70SSascha Wildner sc->aen_q.tail=0; 57933190b70SSascha Wildner sc->aen_q.depth=256; 58033190b70SSascha Wildner sc->aen_q.overflow=0; 58133190b70SSascha Wildner sc->aen_q.q = kmalloc(sizeof(struct tws_event_packet)*sc->aen_q.depth, 58233190b70SSascha Wildner M_TWS, M_WAITOK | M_ZERO); 58333190b70SSascha Wildner return(SUCCESS); 58433190b70SSascha Wildner } 58533190b70SSascha Wildner 58633190b70SSascha Wildner static int 58733190b70SSascha Wildner tws_init_trace_q(struct tws_softc *sc) 58833190b70SSascha Wildner { 58933190b70SSascha Wildner sc->trace_q.head=0; 59033190b70SSascha Wildner sc->trace_q.tail=0; 59133190b70SSascha Wildner sc->trace_q.depth=256; 59233190b70SSascha Wildner sc->trace_q.overflow=0; 59333190b70SSascha Wildner sc->trace_q.q = kmalloc(sizeof(struct tws_trace_rec)*sc->trace_q.depth, 59433190b70SSascha Wildner M_TWS, M_WAITOK | M_ZERO); 59533190b70SSascha Wildner return(SUCCESS); 59633190b70SSascha Wildner } 59733190b70SSascha Wildner 59833190b70SSascha Wildner static int 59933190b70SSascha Wildner tws_init_reqs(struct tws_softc *sc, u_int32_t dma_mem_size) 60033190b70SSascha Wildner { 60133190b70SSascha Wildner 60233190b70SSascha Wildner struct tws_command_packet *cmd_buf; 60333190b70SSascha Wildner cmd_buf = (struct tws_command_packet *)sc->dma_mem; 60433190b70SSascha Wildner int i; 60533190b70SSascha Wildner 60633190b70SSascha Wildner bzero(cmd_buf, dma_mem_size); 60733190b70SSascha Wildner TWS_TRACE_DEBUG(sc, "phy cmd", sc->dma_mem_phys, 0); 60833190b70SSascha Wildner lockmgr(&sc->q_lock, LK_EXCLUSIVE); 60933190b70SSascha Wildner for ( i=0; i< tws_queue_depth; i++) 61033190b70SSascha Wildner { 61133190b70SSascha Wildner if (bus_dmamap_create(sc->data_tag, 0, &sc->reqs[i].dma_map)) { 61233190b70SSascha Wildner /* log a ENOMEM failure msg here */ 6131dd5c540SSascha Wildner lockmgr(&sc->q_lock, LK_RELEASE); 61433190b70SSascha Wildner return(FAILURE); 61533190b70SSascha Wildner } 61633190b70SSascha Wildner sc->reqs[i].cmd_pkt = &cmd_buf[i]; 61733190b70SSascha Wildner 61833190b70SSascha Wildner sc->sense_bufs[i].hdr = &cmd_buf[i].hdr ; 61933190b70SSascha Wildner sc->sense_bufs[i].hdr_pkt_phy = sc->dma_mem_phys + 62033190b70SSascha Wildner (i * sizeof(struct tws_command_packet)); 62133190b70SSascha Wildner sc->sense_bufs[i].posted = false; 62233190b70SSascha Wildner 62333190b70SSascha Wildner sc->reqs[i].cmd_pkt_phy = sc->dma_mem_phys + 62433190b70SSascha Wildner sizeof(struct tws_command_header) + 62533190b70SSascha Wildner (i * sizeof(struct tws_command_packet)); 62633190b70SSascha Wildner sc->reqs[i].request_id = i; 62733190b70SSascha Wildner sc->reqs[i].sc = sc; 62833190b70SSascha Wildner 62933190b70SSascha Wildner sc->reqs[i].cmd_pkt->hdr.header_desc.size_header = 128; 63033190b70SSascha Wildner 63133190b70SSascha Wildner sc->reqs[i].state = TWS_REQ_STATE_FREE; 63233190b70SSascha Wildner if ( i >= TWS_RESERVED_REQS ) 63333190b70SSascha Wildner tws_q_insert_tail(sc, &sc->reqs[i], TWS_FREE_Q); 63433190b70SSascha Wildner } 63533190b70SSascha Wildner lockmgr(&sc->q_lock, LK_RELEASE); 63633190b70SSascha Wildner return(SUCCESS); 63733190b70SSascha Wildner } 63833190b70SSascha Wildner 63933190b70SSascha Wildner static void 64033190b70SSascha Wildner tws_dmamap_cmds_load_cbfn(void *arg, bus_dma_segment_t *segs, 64133190b70SSascha Wildner int nseg, int error) 64233190b70SSascha Wildner { 64333190b70SSascha Wildner 64433190b70SSascha Wildner /* kprintf("command load done \n"); */ 64533190b70SSascha Wildner 64633190b70SSascha Wildner *((bus_addr_t *)arg) = segs[0].ds_addr; 64733190b70SSascha Wildner } 64833190b70SSascha Wildner 64933190b70SSascha Wildner void 65033190b70SSascha Wildner tws_send_event(struct tws_softc *sc, u_int8_t event) 65133190b70SSascha Wildner { 65233190b70SSascha Wildner KKASSERT(lockstatus(&sc->gen_lock, curthread) != 0); 65333190b70SSascha Wildner TWS_TRACE_DEBUG(sc, "received event ", 0, event); 65433190b70SSascha Wildner switch (event) { 65533190b70SSascha Wildner 65633190b70SSascha Wildner case TWS_INIT_START: 65733190b70SSascha Wildner sc->tws_state = TWS_INIT; 65833190b70SSascha Wildner break; 65933190b70SSascha Wildner 66033190b70SSascha Wildner case TWS_INIT_COMPLETE: 66133190b70SSascha Wildner KASSERT(sc->tws_state == TWS_INIT , ("invalid state transition")); 66233190b70SSascha Wildner sc->tws_state = TWS_ONLINE; 66333190b70SSascha Wildner break; 66433190b70SSascha Wildner 66533190b70SSascha Wildner case TWS_RESET_START: 66633190b70SSascha Wildner /* multiple reset ? */ 66733190b70SSascha Wildner KASSERT(sc->tws_state != TWS_RESET, ("invalid state transition")); 66833190b70SSascha Wildner 66933190b70SSascha Wildner /* we can transition to reset state from any state */ 67033190b70SSascha Wildner sc->tws_prev_state = sc->tws_state; 67133190b70SSascha Wildner sc->tws_state = TWS_RESET; 67233190b70SSascha Wildner break; 67333190b70SSascha Wildner 67433190b70SSascha Wildner case TWS_RESET_COMPLETE: 67533190b70SSascha Wildner KASSERT(sc->tws_state == TWS_RESET, ("invalid state transition")); 67633190b70SSascha Wildner sc->tws_state = sc->tws_prev_state; 67733190b70SSascha Wildner break; 67833190b70SSascha Wildner 67933190b70SSascha Wildner case TWS_SCAN_FAILURE: 68033190b70SSascha Wildner KASSERT(sc->tws_state == TWS_ONLINE , ("invalid state transition")); 68133190b70SSascha Wildner sc->tws_state = TWS_OFFLINE; 68233190b70SSascha Wildner break; 68333190b70SSascha Wildner 68433190b70SSascha Wildner case TWS_UNINIT_START: 68533190b70SSascha Wildner KASSERT(sc->tws_state == TWS_ONLINE || sc->tws_state == TWS_OFFLINE, 68633190b70SSascha Wildner ("invalid state transition")); 68733190b70SSascha Wildner sc->tws_state = TWS_UNINIT; 68833190b70SSascha Wildner break; 68933190b70SSascha Wildner } 69033190b70SSascha Wildner 69133190b70SSascha Wildner } 69233190b70SSascha Wildner 69333190b70SSascha Wildner uint8_t 69433190b70SSascha Wildner tws_get_state(struct tws_softc *sc) 69533190b70SSascha Wildner { 69633190b70SSascha Wildner 69733190b70SSascha Wildner return((u_int8_t)sc->tws_state); 69833190b70SSascha Wildner 69933190b70SSascha Wildner } 70033190b70SSascha Wildner 70133190b70SSascha Wildner /* Called during system shutdown after sync. */ 70233190b70SSascha Wildner 70333190b70SSascha Wildner static int 70433190b70SSascha Wildner tws_shutdown(device_t dev) 70533190b70SSascha Wildner { 70633190b70SSascha Wildner 70733190b70SSascha Wildner struct tws_softc *sc = device_get_softc(dev); 70833190b70SSascha Wildner 70933190b70SSascha Wildner TWS_TRACE_DEBUG(sc, "entry", 0, 0); 71033190b70SSascha Wildner 71133190b70SSascha Wildner tws_turn_off_interrupts(sc); 71233190b70SSascha Wildner tws_init_connect(sc, 1); 71333190b70SSascha Wildner 71433190b70SSascha Wildner return (0); 71533190b70SSascha Wildner } 71633190b70SSascha Wildner 71733190b70SSascha Wildner /* 71833190b70SSascha Wildner * Device suspend routine. 71933190b70SSascha Wildner */ 72033190b70SSascha Wildner static int 72133190b70SSascha Wildner tws_suspend(device_t dev) 72233190b70SSascha Wildner { 72333190b70SSascha Wildner struct tws_softc *sc = device_get_softc(dev); 72433190b70SSascha Wildner 72533190b70SSascha Wildner if ( sc ) 72633190b70SSascha Wildner TWS_TRACE_DEBUG(sc, "entry", 0, 0); 72733190b70SSascha Wildner return (0); 72833190b70SSascha Wildner } 72933190b70SSascha Wildner 73033190b70SSascha Wildner /* 73133190b70SSascha Wildner * Device resume routine. 73233190b70SSascha Wildner */ 73333190b70SSascha Wildner static int 73433190b70SSascha Wildner tws_resume(device_t dev) 73533190b70SSascha Wildner { 73633190b70SSascha Wildner 73733190b70SSascha Wildner struct tws_softc *sc = device_get_softc(dev); 73833190b70SSascha Wildner 73933190b70SSascha Wildner if ( sc ) 74033190b70SSascha Wildner TWS_TRACE_DEBUG(sc, "entry", 0, 0); 74133190b70SSascha Wildner return (0); 74233190b70SSascha Wildner } 74333190b70SSascha Wildner 74433190b70SSascha Wildner 74533190b70SSascha Wildner struct tws_request * 74633190b70SSascha Wildner tws_get_request(struct tws_softc *sc, u_int16_t type) 74733190b70SSascha Wildner { 74833190b70SSascha Wildner struct tws_request *r = NULL; 74933190b70SSascha Wildner 75033190b70SSascha Wildner switch ( type ) { 75133190b70SSascha Wildner case TWS_INTERNAL_CMD_REQ : 75233190b70SSascha Wildner lockmgr(&sc->gen_lock, LK_EXCLUSIVE); 75333190b70SSascha Wildner r = &sc->reqs[0]; 75433190b70SSascha Wildner if ( r->state != TWS_REQ_STATE_FREE ) { 75533190b70SSascha Wildner r = NULL; 75633190b70SSascha Wildner } else { 75733190b70SSascha Wildner r->state = TWS_REQ_STATE_BUSY; 75833190b70SSascha Wildner } 75933190b70SSascha Wildner lockmgr(&sc->gen_lock, LK_RELEASE); 76033190b70SSascha Wildner break; 76133190b70SSascha Wildner case TWS_AEN_FETCH_REQ : 76233190b70SSascha Wildner lockmgr(&sc->gen_lock, LK_EXCLUSIVE); 76333190b70SSascha Wildner r = &sc->reqs[1]; 76433190b70SSascha Wildner if ( r->state != TWS_REQ_STATE_FREE ) { 76533190b70SSascha Wildner r = NULL; 76633190b70SSascha Wildner } else { 76733190b70SSascha Wildner r->state = TWS_REQ_STATE_BUSY; 76833190b70SSascha Wildner } 76933190b70SSascha Wildner lockmgr(&sc->gen_lock, LK_RELEASE); 77033190b70SSascha Wildner break; 77133190b70SSascha Wildner case TWS_PASSTHRU_REQ : 77233190b70SSascha Wildner lockmgr(&sc->gen_lock, LK_EXCLUSIVE); 77333190b70SSascha Wildner r = &sc->reqs[2]; 77433190b70SSascha Wildner if ( r->state != TWS_REQ_STATE_FREE ) { 77533190b70SSascha Wildner r = NULL; 77633190b70SSascha Wildner } else { 77733190b70SSascha Wildner r->state = TWS_REQ_STATE_BUSY; 77833190b70SSascha Wildner } 77933190b70SSascha Wildner lockmgr(&sc->gen_lock, LK_RELEASE); 78033190b70SSascha Wildner break; 78133190b70SSascha Wildner case TWS_GETSET_PARAM_REQ : 78233190b70SSascha Wildner lockmgr(&sc->gen_lock, LK_EXCLUSIVE); 78333190b70SSascha Wildner r = &sc->reqs[3]; 78433190b70SSascha Wildner if ( r->state != TWS_REQ_STATE_FREE ) { 78533190b70SSascha Wildner r = NULL; 78633190b70SSascha Wildner } else { 78733190b70SSascha Wildner r->state = TWS_REQ_STATE_BUSY; 78833190b70SSascha Wildner } 78933190b70SSascha Wildner lockmgr(&sc->gen_lock, LK_RELEASE); 79033190b70SSascha Wildner break; 79133190b70SSascha Wildner case TWS_SCSI_IO_REQ : 79233190b70SSascha Wildner lockmgr(&sc->q_lock, LK_EXCLUSIVE); 79333190b70SSascha Wildner r = tws_q_remove_head(sc, TWS_FREE_Q); 79433190b70SSascha Wildner if ( r ) 79533190b70SSascha Wildner r->state = TWS_REQ_STATE_TRAN; 79633190b70SSascha Wildner lockmgr(&sc->q_lock, LK_RELEASE); 79733190b70SSascha Wildner break; 79833190b70SSascha Wildner default : 79933190b70SSascha Wildner TWS_TRACE_DEBUG(sc, "Unknown req type", 0, type); 80033190b70SSascha Wildner r = NULL; 80133190b70SSascha Wildner 80233190b70SSascha Wildner } 80333190b70SSascha Wildner 80433190b70SSascha Wildner if ( r ) { 80533190b70SSascha Wildner bzero(&r->cmd_pkt->cmd, sizeof(struct tws_command_apache)); 80633190b70SSascha Wildner callout_init(&r->thandle); 80733190b70SSascha Wildner r->data = NULL; 80833190b70SSascha Wildner r->length = 0; 80933190b70SSascha Wildner r->type = type; 81033190b70SSascha Wildner r->flags = TWS_DIR_UNKNOWN; 81133190b70SSascha Wildner r->error_code = TWS_REQ_ERR_INVALID; 81233190b70SSascha Wildner r->ccb_ptr = NULL; 81333190b70SSascha Wildner r->cb = NULL; 81433190b70SSascha Wildner r->next = r->prev = NULL; 81533190b70SSascha Wildner } 81633190b70SSascha Wildner return(r); 81733190b70SSascha Wildner } 81833190b70SSascha Wildner 81933190b70SSascha Wildner void 82033190b70SSascha Wildner tws_release_request(struct tws_request *req) 82133190b70SSascha Wildner { 82233190b70SSascha Wildner 82333190b70SSascha Wildner struct tws_softc *sc = req->sc; 82433190b70SSascha Wildner 82533190b70SSascha Wildner TWS_TRACE_DEBUG(sc, "entry", sc, 0); 82633190b70SSascha Wildner lockmgr(&sc->q_lock, LK_EXCLUSIVE); 82733190b70SSascha Wildner tws_q_insert_tail(sc, req, TWS_FREE_Q); 82833190b70SSascha Wildner lockmgr(&sc->q_lock, LK_RELEASE); 82933190b70SSascha Wildner } 83033190b70SSascha Wildner 83133190b70SSascha Wildner static device_method_t tws_methods[] = { 83233190b70SSascha Wildner /* Device interface */ 83333190b70SSascha Wildner DEVMETHOD(device_probe, tws_probe), 83433190b70SSascha Wildner DEVMETHOD(device_attach, tws_attach), 83533190b70SSascha Wildner DEVMETHOD(device_detach, tws_detach), 83633190b70SSascha Wildner DEVMETHOD(device_shutdown, tws_shutdown), 83733190b70SSascha Wildner DEVMETHOD(device_suspend, tws_suspend), 83833190b70SSascha Wildner DEVMETHOD(device_resume, tws_resume), 83933190b70SSascha Wildner 84033190b70SSascha Wildner DEVMETHOD(bus_print_child, bus_generic_print_child), 84133190b70SSascha Wildner DEVMETHOD(bus_driver_added, bus_generic_driver_added), 842d3c9c58eSSascha Wildner DEVMETHOD_END 84333190b70SSascha Wildner }; 84433190b70SSascha Wildner 84533190b70SSascha Wildner static driver_t tws_driver = { 84633190b70SSascha Wildner "tws", 84733190b70SSascha Wildner tws_methods, 84833190b70SSascha Wildner sizeof(struct tws_softc) 84933190b70SSascha Wildner }; 85033190b70SSascha Wildner 85133190b70SSascha Wildner 85233190b70SSascha Wildner static devclass_t tws_devclass; 85333190b70SSascha Wildner 85433190b70SSascha Wildner /* DEFINE_CLASS_0(tws, tws_driver, tws_methods, sizeof(struct tws_softc)); */ 855aa2b9d05SSascha Wildner DRIVER_MODULE(tws, pci, tws_driver, tws_devclass, NULL, NULL); 85633190b70SSascha Wildner MODULE_DEPEND(tws, cam, 1, 1, 1); 85733190b70SSascha Wildner MODULE_DEPEND(tws, pci, 1, 1, 1); 85833190b70SSascha Wildner 85933190b70SSascha Wildner TUNABLE_INT("hw.tws.queue_depth", &tws_queue_depth); 8605ef3096fSSascha Wildner TUNABLE_INT("hw.tws.msi.enable", &tws_msi_enable); 861