xref: /dflybsd-src/sys/dev/raid/tws/tws.c (revision 030b0c8c4cf27c560ccec70410c8e21934ae677d)
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>
43*cec957e9SMatthew Dillon #include <bus/cam/cam_xpt.h>
44*cec957e9SMatthew Dillon #include <bus/cam/cam_xpt_periph.h>
4533190b70SSascha Wildner 
465ef3096fSSascha Wildner static int	tws_msi_enable = 1;
475ef3096fSSascha Wildner 
4833190b70SSascha Wildner MALLOC_DEFINE(M_TWS, "twsbuf", "buffers used by tws driver");
4933190b70SSascha Wildner int tws_queue_depth = TWS_MAX_REQS;
5033190b70SSascha Wildner 
5133190b70SSascha Wildner /* externs */
5233190b70SSascha Wildner extern int tws_cam_attach(struct tws_softc *sc);
5333190b70SSascha Wildner extern void tws_cam_detach(struct tws_softc *sc);
5433190b70SSascha Wildner extern int tws_init_ctlr(struct tws_softc *sc);
5533190b70SSascha Wildner extern boolean tws_ctlr_ready(struct tws_softc *sc);
5633190b70SSascha Wildner extern void tws_turn_off_interrupts(struct tws_softc *sc);
5733190b70SSascha Wildner extern void tws_q_insert_tail(struct tws_softc *sc, struct tws_request *req,
5833190b70SSascha Wildner                                 u_int8_t q_type );
5933190b70SSascha Wildner extern struct tws_request *tws_q_remove_request(struct tws_softc *sc,
6033190b70SSascha Wildner                                    struct tws_request *req, u_int8_t q_type );
6133190b70SSascha Wildner extern struct tws_request *tws_q_remove_head(struct tws_softc *sc,
6233190b70SSascha Wildner                                                        u_int8_t q_type );
6333190b70SSascha Wildner extern boolean tws_get_response(struct tws_softc *sc, u_int16_t *req_id);
6433190b70SSascha Wildner extern boolean tws_ctlr_reset(struct tws_softc *sc);
6533190b70SSascha Wildner extern void tws_intr(void *arg);
6633190b70SSascha Wildner extern int tws_use_32bit_sgls;
6733190b70SSascha Wildner 
6833190b70SSascha Wildner 
6933190b70SSascha Wildner struct tws_request *tws_get_request(struct tws_softc *sc, u_int16_t type);
7033190b70SSascha Wildner int tws_init_connect(struct tws_softc *sc, u_int16_t mc);
7133190b70SSascha Wildner void tws_send_event(struct tws_softc *sc, u_int8_t event);
7233190b70SSascha Wildner uint8_t tws_get_state(struct tws_softc *sc);
7333190b70SSascha Wildner void tws_release_request(struct tws_request *req);
7433190b70SSascha Wildner 
7533190b70SSascha Wildner 
7633190b70SSascha Wildner 
7733190b70SSascha Wildner /* Function prototypes */
7833190b70SSascha Wildner static d_open_t     tws_open;
7933190b70SSascha Wildner static d_close_t    tws_close;
8033190b70SSascha Wildner static d_read_t     tws_read;
8133190b70SSascha Wildner static d_write_t    tws_write;
8233190b70SSascha Wildner extern d_ioctl_t    tws_ioctl;
8333190b70SSascha Wildner 
8433190b70SSascha Wildner static int tws_init(struct tws_softc *sc);
8533190b70SSascha Wildner static void tws_dmamap_cmds_load_cbfn(void *arg, bus_dma_segment_t *segs,
8633190b70SSascha Wildner                            int nseg, int error);
8733190b70SSascha Wildner 
8833190b70SSascha Wildner static int tws_init_reqs(struct tws_softc *sc, u_int32_t dma_mem_size);
8933190b70SSascha Wildner static int tws_init_aen_q(struct tws_softc *sc);
9033190b70SSascha Wildner static int tws_init_trace_q(struct tws_softc *sc);
9133190b70SSascha Wildner static int tws_setup_irq(struct tws_softc *sc);
9233190b70SSascha Wildner 
9333190b70SSascha Wildner 
9433190b70SSascha Wildner /* Character device entry points */
9533190b70SSascha Wildner 
9633190b70SSascha Wildner static struct dev_ops tws_ops = {
9733190b70SSascha Wildner     { "tws", 0, 0 },
9833190b70SSascha Wildner     .d_open =   tws_open,
9933190b70SSascha Wildner     .d_close =  tws_close,
10033190b70SSascha Wildner     .d_read =   tws_read,
10133190b70SSascha Wildner     .d_write =  tws_write,
10233190b70SSascha Wildner     .d_ioctl =  tws_ioctl,
10333190b70SSascha Wildner };
10433190b70SSascha Wildner 
10533190b70SSascha Wildner /*
10633190b70SSascha Wildner  * In the cdevsw routines, we find our softc by using the si_drv1 member
10733190b70SSascha Wildner  * of struct cdev.  We set this variable to point to our softc in our
10833190b70SSascha Wildner  * attach routine when we create the /dev entry.
10933190b70SSascha Wildner  */
11033190b70SSascha Wildner 
1118406cf70SSascha Wildner static int
tws_open(struct dev_open_args * ap)11233190b70SSascha Wildner tws_open(struct dev_open_args *ap)
11333190b70SSascha Wildner {
11433190b70SSascha Wildner     cdev_t dev = ap->a_head.a_dev;
11533190b70SSascha Wildner     struct tws_softc *sc = dev->si_drv1;
11633190b70SSascha Wildner 
11733190b70SSascha Wildner     if ( sc )
11833190b70SSascha Wildner         TWS_TRACE_DEBUG(sc, "entry", dev, oflags);
11933190b70SSascha Wildner     return (0);
12033190b70SSascha Wildner }
12133190b70SSascha Wildner 
1228406cf70SSascha Wildner static int
tws_close(struct dev_close_args * ap)12333190b70SSascha Wildner tws_close(struct dev_close_args *ap)
12433190b70SSascha Wildner {
12533190b70SSascha Wildner     cdev_t dev = ap->a_head.a_dev;
12633190b70SSascha Wildner     struct tws_softc *sc = dev->si_drv1;
12733190b70SSascha Wildner 
12833190b70SSascha Wildner     if ( sc )
12933190b70SSascha Wildner         TWS_TRACE_DEBUG(sc, "entry", dev, fflag);
13033190b70SSascha Wildner     return (0);
13133190b70SSascha Wildner }
13233190b70SSascha Wildner 
1338406cf70SSascha Wildner static int
tws_read(struct dev_read_args * ap)13433190b70SSascha Wildner tws_read(struct dev_read_args *ap)
13533190b70SSascha Wildner {
13633190b70SSascha Wildner     cdev_t dev = ap->a_head.a_dev;
13733190b70SSascha Wildner     struct tws_softc *sc = dev->si_drv1;
13833190b70SSascha Wildner 
13933190b70SSascha Wildner     if ( sc )
14033190b70SSascha Wildner         TWS_TRACE_DEBUG(sc, "entry", dev, ioflag);
14133190b70SSascha Wildner     return (0);
14233190b70SSascha Wildner }
14333190b70SSascha Wildner 
1448406cf70SSascha Wildner static int
tws_write(struct dev_write_args * ap)14533190b70SSascha Wildner tws_write(struct dev_write_args *ap)
14633190b70SSascha Wildner {
14733190b70SSascha Wildner     cdev_t dev = ap->a_head.a_dev;
14833190b70SSascha Wildner     struct tws_softc *sc = dev->si_drv1;
14933190b70SSascha Wildner 
15033190b70SSascha Wildner     if ( sc )
15133190b70SSascha Wildner         TWS_TRACE_DEBUG(sc, "entry", dev, ioflag);
15233190b70SSascha Wildner     return (0);
15333190b70SSascha Wildner }
15433190b70SSascha Wildner 
15533190b70SSascha Wildner /* PCI Support Functions */
15633190b70SSascha Wildner 
15733190b70SSascha Wildner /*
15833190b70SSascha Wildner  * Compare the device ID of this device against the IDs that this driver
15933190b70SSascha Wildner  * supports.  If there is a match, set the description and return success.
16033190b70SSascha Wildner  */
16133190b70SSascha Wildner static int
tws_probe(device_t dev)16233190b70SSascha Wildner tws_probe(device_t dev)
16333190b70SSascha Wildner {
16433190b70SSascha Wildner     static u_int8_t first_ctlr = 1;
16533190b70SSascha Wildner 
16633190b70SSascha Wildner     if ((pci_get_vendor(dev) == TWS_VENDOR_ID) &&
16733190b70SSascha Wildner         (pci_get_device(dev) == TWS_DEVICE_ID)) {
16833190b70SSascha Wildner         device_set_desc(dev, "LSI 3ware SAS/SATA Storage Controller");
16933190b70SSascha Wildner         if (first_ctlr) {
17033190b70SSascha Wildner             kprintf("LSI 3ware device driver for SAS/SATA storage "
17133190b70SSascha Wildner                     "controllers, version: %s\n", TWS_DRIVER_VERSION_STRING);
17233190b70SSascha Wildner             first_ctlr = 0;
17333190b70SSascha Wildner         }
17433190b70SSascha Wildner 
17533190b70SSascha Wildner         return(0);
17633190b70SSascha Wildner     }
17733190b70SSascha Wildner     return (ENXIO);
17833190b70SSascha Wildner }
17933190b70SSascha Wildner 
18033190b70SSascha Wildner /* Attach function is only called if the probe is successful. */
18133190b70SSascha Wildner 
18233190b70SSascha Wildner static int
tws_attach(device_t dev)18333190b70SSascha Wildner tws_attach(device_t dev)
18433190b70SSascha Wildner {
18533190b70SSascha Wildner     struct tws_softc *sc = device_get_softc(dev);
18633190b70SSascha Wildner     u_int32_t cmd, bar;
1875ef3096fSSascha Wildner     int error=0;
18833190b70SSascha Wildner 
18933190b70SSascha Wildner     /* no tracing yet */
19033190b70SSascha Wildner     /* Look up our softc and initialize its fields. */
19133190b70SSascha Wildner     sc->tws_dev = dev;
19233190b70SSascha Wildner     sc->device_id = pci_get_device(dev);
19333190b70SSascha Wildner     sc->subvendor_id = pci_get_subvendor(dev);
19433190b70SSascha Wildner     sc->subdevice_id = pci_get_subdevice(dev);
19533190b70SSascha Wildner 
19633190b70SSascha Wildner     /* Intialize mutexes */
19733190b70SSascha Wildner     lockinit(&sc->q_lock, "tws_q_lock", 0, LK_CANRECURSE);
19833190b70SSascha Wildner     lockinit(&sc->sim_lock, "tws_sim_lock", 0, LK_CANRECURSE);
19933190b70SSascha Wildner     lockinit(&sc->gen_lock, "tws_gen_lock", 0, LK_CANRECURSE);
20033190b70SSascha Wildner     lockinit(&sc->io_lock, "tws_io_lock", 0, LK_CANRECURSE);
20133190b70SSascha Wildner 
20233190b70SSascha Wildner     if ( tws_init_trace_q(sc) == FAILURE )
20333190b70SSascha Wildner         kprintf("trace init failure\n");
20433190b70SSascha Wildner     /* send init event */
20533190b70SSascha Wildner     lockmgr(&sc->gen_lock, LK_EXCLUSIVE);
20633190b70SSascha Wildner     tws_send_event(sc, TWS_INIT_START);
20733190b70SSascha Wildner     lockmgr(&sc->gen_lock, LK_RELEASE);
20833190b70SSascha Wildner 
20933190b70SSascha Wildner 
21033190b70SSascha Wildner #if _BYTE_ORDER == _BIG_ENDIAN
21133190b70SSascha Wildner     TWS_TRACE(sc, "BIG endian", 0, 0);
21233190b70SSascha Wildner #endif
21326595b18SSascha Wildner     SYSCTL_ADD_STRING(device_get_sysctl_ctx(dev),
21426595b18SSascha Wildner 		      SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
21533190b70SSascha Wildner                       OID_AUTO, "driver_version", CTLFLAG_RD,
21633190b70SSascha Wildner                       TWS_DRIVER_VERSION_STRING, 0, "TWS driver version");
21733190b70SSascha Wildner 
21833190b70SSascha Wildner     cmd = pci_read_config(dev, PCIR_COMMAND, 2);
21933190b70SSascha Wildner     if ( (cmd & PCIM_CMD_PORTEN) == 0) {
22033190b70SSascha Wildner         tws_log(sc, PCI_COMMAND_READ);
22133190b70SSascha Wildner         goto attach_fail_1;
22233190b70SSascha Wildner     }
22333190b70SSascha Wildner     /* Force the busmaster enable bit on. */
22433190b70SSascha Wildner     cmd |= PCIM_CMD_BUSMASTEREN;
22533190b70SSascha Wildner     pci_write_config(dev, PCIR_COMMAND, cmd, 2);
22633190b70SSascha Wildner 
22733190b70SSascha Wildner     bar = pci_read_config(dev, TWS_PCI_BAR0, 4);
22833190b70SSascha Wildner     TWS_TRACE_DEBUG(sc, "bar0 ", bar, 0);
22933190b70SSascha Wildner     bar = pci_read_config(dev, TWS_PCI_BAR1, 4);
23033190b70SSascha Wildner     bar = bar & ~TWS_BIT2;
23133190b70SSascha Wildner     TWS_TRACE_DEBUG(sc, "bar1 ", bar, 0);
23233190b70SSascha Wildner 
23333190b70SSascha Wildner     /* MFA base address is BAR2 register used for
23433190b70SSascha Wildner      * push mode. Firmware will evatualy move to
23533190b70SSascha Wildner      * pull mode during witch this needs to change
23633190b70SSascha Wildner      */
23733190b70SSascha Wildner #ifndef TWS_PULL_MODE_ENABLE
23833190b70SSascha Wildner     sc->mfa_base = (u_int64_t)pci_read_config(dev, TWS_PCI_BAR2, 4);
23933190b70SSascha Wildner     sc->mfa_base = sc->mfa_base & ~TWS_BIT2;
24033190b70SSascha Wildner     TWS_TRACE_DEBUG(sc, "bar2 ", sc->mfa_base, 0);
24133190b70SSascha Wildner #endif
24233190b70SSascha Wildner 
24333190b70SSascha Wildner     /* allocate MMIO register space */
24433190b70SSascha Wildner     sc->reg_res_id = TWS_PCI_BAR1; /* BAR1 offset */
24533190b70SSascha Wildner     if ((sc->reg_res = bus_alloc_resource(dev, SYS_RES_MEMORY,
24633190b70SSascha Wildner                                 &(sc->reg_res_id), 0, ~0, 1, RF_ACTIVE))
24733190b70SSascha Wildner                                 == NULL) {
24833190b70SSascha Wildner         tws_log(sc, ALLOC_MEMORY_RES);
24933190b70SSascha Wildner         goto attach_fail_1;
25033190b70SSascha Wildner     }
25133190b70SSascha Wildner     sc->bus_tag = rman_get_bustag(sc->reg_res);
25233190b70SSascha Wildner     sc->bus_handle = rman_get_bushandle(sc->reg_res);
25333190b70SSascha Wildner 
25433190b70SSascha Wildner #ifndef TWS_PULL_MODE_ENABLE
25533190b70SSascha Wildner     /* Allocate bus space for inbound mfa */
25633190b70SSascha Wildner     sc->mfa_res_id = TWS_PCI_BAR2; /* BAR2 offset */
25733190b70SSascha Wildner     if ((sc->mfa_res = bus_alloc_resource(dev, SYS_RES_MEMORY,
25833190b70SSascha Wildner                           &(sc->mfa_res_id), 0, ~0, 0x100000, RF_ACTIVE))
25933190b70SSascha Wildner                                 == NULL) {
26033190b70SSascha Wildner         tws_log(sc, ALLOC_MEMORY_RES);
26133190b70SSascha Wildner         goto attach_fail_2;
26233190b70SSascha Wildner     }
26333190b70SSascha Wildner     sc->bus_mfa_tag = rman_get_bustag(sc->mfa_res);
26433190b70SSascha Wildner     sc->bus_mfa_handle = rman_get_bushandle(sc->mfa_res);
26533190b70SSascha Wildner #endif
26633190b70SSascha Wildner 
26733190b70SSascha Wildner     /* Allocate and register our interrupt. */
26833190b70SSascha Wildner     if ( tws_setup_irq(sc) == FAILURE ) {
26933190b70SSascha Wildner         tws_log(sc, ALLOC_MEMORY_RES);
27033190b70SSascha Wildner         goto attach_fail_3;
27133190b70SSascha Wildner     }
27233190b70SSascha Wildner 
27333190b70SSascha Wildner     /* Init callouts. */
27433190b70SSascha Wildner     callout_init(&sc->print_stats_handle);
27533190b70SSascha Wildner     callout_init(&sc->reset_cb_handle);
27633190b70SSascha Wildner     callout_init(&sc->reinit_handle);
27733190b70SSascha Wildner 
27833190b70SSascha Wildner     /*
27933190b70SSascha Wildner      * Create a /dev entry for this device.  The kernel will assign us
28033190b70SSascha Wildner      * a major number automatically.  We use the unit number of this
28133190b70SSascha Wildner      * device as the minor number and name the character device
28233190b70SSascha Wildner      * "tws<unit>".
28333190b70SSascha Wildner      */
28433190b70SSascha Wildner     sc->tws_cdev = make_dev(&tws_ops, device_get_unit(dev),
28533190b70SSascha Wildner         UID_ROOT, GID_OPERATOR, S_IRUSR | S_IWUSR, "tws%u",
28633190b70SSascha Wildner         device_get_unit(dev));
28733190b70SSascha Wildner     sc->tws_cdev->si_drv1 = sc;
28833190b70SSascha Wildner 
28933190b70SSascha Wildner     if ( tws_init(sc) == FAILURE ) {
29033190b70SSascha Wildner         tws_log(sc, TWS_INIT_FAILURE);
29133190b70SSascha Wildner         goto attach_fail_4;
29233190b70SSascha Wildner     }
29333190b70SSascha Wildner     if ( tws_init_ctlr(sc) == FAILURE ) {
29433190b70SSascha Wildner         tws_log(sc, TWS_CTLR_INIT_FAILURE);
29533190b70SSascha Wildner         goto attach_fail_4;
29633190b70SSascha Wildner     }
29733190b70SSascha Wildner     if ((error = tws_cam_attach(sc))) {
29833190b70SSascha Wildner         tws_log(sc, TWS_CAM_ATTACH);
29933190b70SSascha Wildner         goto attach_fail_4;
30033190b70SSascha Wildner     }
30133190b70SSascha Wildner     /* send init complete event */
30233190b70SSascha Wildner     lockmgr(&sc->gen_lock, LK_EXCLUSIVE);
30333190b70SSascha Wildner     tws_send_event(sc, TWS_INIT_COMPLETE);
30433190b70SSascha Wildner     lockmgr(&sc->gen_lock, LK_RELEASE);
30533190b70SSascha Wildner 
30633190b70SSascha Wildner     TWS_TRACE_DEBUG(sc, "attached successfully", 0, sc->device_id);
30733190b70SSascha Wildner     return(0);
30833190b70SSascha Wildner 
30933190b70SSascha Wildner attach_fail_4:
3105ef3096fSSascha Wildner     if (sc->intr_handle) {
31133190b70SSascha Wildner         if ((error = bus_teardown_intr(sc->tws_dev,
3125ef3096fSSascha Wildner                      sc->irq_res, sc->intr_handle)))
31333190b70SSascha Wildner             TWS_TRACE(sc, "bus teardown intr", 0, error);
31433190b70SSascha Wildner     }
31533190b70SSascha Wildner     destroy_dev(sc->tws_cdev);
31633190b70SSascha Wildner     dev_ops_remove_minor(&tws_ops, device_get_unit(sc->tws_dev));
31733190b70SSascha Wildner attach_fail_3:
3185ef3096fSSascha Wildner     if (sc->irq_res) {
31933190b70SSascha Wildner         if (bus_release_resource(sc->tws_dev,
3205ef3096fSSascha Wildner             SYS_RES_IRQ, sc->irq_res_id, sc->irq_res))
32133190b70SSascha Wildner             TWS_TRACE(sc, "bus irq res", 0, 0);
32233190b70SSascha Wildner     }
32333190b70SSascha Wildner #ifndef TWS_PULL_MODE_ENABLE
32433190b70SSascha Wildner attach_fail_2:
32533190b70SSascha Wildner #endif
32633190b70SSascha Wildner     if ( sc->mfa_res ){
32733190b70SSascha Wildner         if (bus_release_resource(sc->tws_dev,
32833190b70SSascha Wildner                  SYS_RES_MEMORY, sc->mfa_res_id, sc->mfa_res))
32933190b70SSascha Wildner             TWS_TRACE(sc, "bus release ", 0, sc->mfa_res_id);
33033190b70SSascha Wildner     }
33133190b70SSascha Wildner     if ( sc->reg_res ){
33233190b70SSascha Wildner         if (bus_release_resource(sc->tws_dev,
33333190b70SSascha Wildner                  SYS_RES_MEMORY, sc->reg_res_id, sc->reg_res))
33433190b70SSascha Wildner             TWS_TRACE(sc, "bus release2 ", 0, sc->reg_res_id);
33533190b70SSascha Wildner     }
33633190b70SSascha Wildner attach_fail_1:
33733190b70SSascha Wildner     lockuninit(&sc->q_lock);
33833190b70SSascha Wildner     lockuninit(&sc->sim_lock);
33933190b70SSascha Wildner     lockuninit(&sc->gen_lock);
34033190b70SSascha Wildner     lockuninit(&sc->io_lock);
34133190b70SSascha Wildner     return (ENXIO);
34233190b70SSascha Wildner }
34333190b70SSascha Wildner 
34433190b70SSascha Wildner /* Detach device. */
34533190b70SSascha Wildner 
34633190b70SSascha Wildner static int
tws_detach(device_t dev)34733190b70SSascha Wildner tws_detach(device_t dev)
34833190b70SSascha Wildner {
34933190b70SSascha Wildner     struct tws_softc *sc = device_get_softc(dev);
3505ef3096fSSascha Wildner     int error;
35133190b70SSascha Wildner     u_int32_t reg;
35233190b70SSascha Wildner 
35333190b70SSascha Wildner     TWS_TRACE_DEBUG(sc, "entry", 0, 0);
35433190b70SSascha Wildner 
35533190b70SSascha Wildner     lockmgr(&sc->gen_lock, LK_EXCLUSIVE);
35633190b70SSascha Wildner     tws_send_event(sc, TWS_UNINIT_START);
35733190b70SSascha Wildner     lockmgr(&sc->gen_lock, LK_RELEASE);
35833190b70SSascha Wildner 
35933190b70SSascha Wildner     /* needs to disable interrupt before detaching from cam */
36033190b70SSascha Wildner     tws_turn_off_interrupts(sc);
36133190b70SSascha Wildner     /* clear door bell */
36233190b70SSascha Wildner     tws_write_reg(sc, TWS_I2O0_HOBDBC, ~0, 4);
36333190b70SSascha Wildner     reg = tws_read_reg(sc, TWS_I2O0_HIMASK, 4);
36433190b70SSascha Wildner     TWS_TRACE_DEBUG(sc, "turn-off-intr", reg, 0);
36533190b70SSascha Wildner     sc->obfl_q_overrun = false;
36633190b70SSascha Wildner     tws_init_connect(sc, 1);
36733190b70SSascha Wildner 
36833190b70SSascha Wildner     /* Teardown the state in our softc created in our attach routine. */
36933190b70SSascha Wildner     /* Disconnect the interrupt handler. */
3705ef3096fSSascha Wildner     if (sc->intr_handle) {
37133190b70SSascha Wildner         if ((error = bus_teardown_intr(sc->tws_dev,
3725ef3096fSSascha Wildner                      sc->irq_res, sc->intr_handle)))
37333190b70SSascha Wildner             TWS_TRACE(sc, "bus teardown intr", 0, error);
37433190b70SSascha Wildner     }
37533190b70SSascha Wildner     /* Release irq resource */
3765ef3096fSSascha Wildner     if (sc->irq_res) {
37733190b70SSascha Wildner         if (bus_release_resource(sc->tws_dev,
3785ef3096fSSascha Wildner                  SYS_RES_IRQ, sc->irq_res_id, sc->irq_res))
3795ef3096fSSascha Wildner             TWS_TRACE(sc, "bus release irq resource", 0, sc->irq_res_id);
38033190b70SSascha Wildner     }
3815ef3096fSSascha Wildner     if (sc->intr_type == PCI_INTR_TYPE_MSI)
38233190b70SSascha Wildner         pci_release_msi(sc->tws_dev);
38333190b70SSascha Wildner 
38433190b70SSascha Wildner     tws_cam_detach(sc);
38533190b70SSascha Wildner 
38633190b70SSascha Wildner     /* Release memory resource */
38733190b70SSascha Wildner     if ( sc->mfa_res ){
38833190b70SSascha Wildner         if (bus_release_resource(sc->tws_dev,
38933190b70SSascha Wildner                  SYS_RES_MEMORY, sc->mfa_res_id, sc->mfa_res))
39033190b70SSascha Wildner             TWS_TRACE(sc, "bus release mem resource", 0, sc->mfa_res_id);
39133190b70SSascha Wildner     }
39233190b70SSascha Wildner     if ( sc->reg_res ){
39333190b70SSascha Wildner         if (bus_release_resource(sc->tws_dev,
39433190b70SSascha Wildner                  SYS_RES_MEMORY, sc->reg_res_id, sc->reg_res))
39533190b70SSascha Wildner             TWS_TRACE(sc, "bus release mem resource", 0, sc->reg_res_id);
39633190b70SSascha Wildner     }
39733190b70SSascha Wildner 
39833190b70SSascha Wildner     kfree(sc->reqs, M_TWS);
39933190b70SSascha Wildner     kfree(sc->sense_bufs, M_TWS);
400*cec957e9SMatthew Dillon     xpt_free_ccb(&sc->scan_ccb->ccb_h);
40133190b70SSascha Wildner     kfree(sc->aen_q.q, M_TWS);
40233190b70SSascha Wildner     kfree(sc->trace_q.q, M_TWS);
40333190b70SSascha Wildner     lockuninit(&sc->q_lock);
40433190b70SSascha Wildner     lockuninit(&sc->sim_lock);
40533190b70SSascha Wildner     lockuninit(&sc->gen_lock);
40633190b70SSascha Wildner     lockuninit(&sc->io_lock);
40733190b70SSascha Wildner     destroy_dev(sc->tws_cdev);
40833190b70SSascha Wildner     dev_ops_remove_minor(&tws_ops, device_get_unit(sc->tws_dev));
40933190b70SSascha Wildner     return (0);
41033190b70SSascha Wildner }
41133190b70SSascha Wildner 
41233190b70SSascha Wildner static int
tws_setup_irq(struct tws_softc * sc)41333190b70SSascha Wildner tws_setup_irq(struct tws_softc *sc)
41433190b70SSascha Wildner {
41533190b70SSascha Wildner     u_int16_t cmd;
4165ef3096fSSascha Wildner     u_int irq_flags;
41733190b70SSascha Wildner 
41833190b70SSascha Wildner     cmd = pci_read_config(sc->tws_dev, PCIR_COMMAND, 2);
4195ef3096fSSascha Wildner 
4205ef3096fSSascha Wildner     if (tws_msi_enable)
4215ef3096fSSascha Wildner         cmd |= 0x0400;
4225ef3096fSSascha Wildner     else
4235ef3096fSSascha Wildner         cmd &= ~0x0400;
42433190b70SSascha Wildner     pci_write_config(sc->tws_dev, PCIR_COMMAND, cmd, 2);
4255ef3096fSSascha Wildner     sc->irq_res = 0;
4265ef3096fSSascha Wildner     sc->intr_type = pci_alloc_1intr(sc->tws_dev, tws_msi_enable,
4275ef3096fSSascha Wildner         &sc->irq_res_id, &irq_flags);
4285ef3096fSSascha Wildner     sc->irq_res = bus_alloc_resource_any(sc->tws_dev, SYS_RES_IRQ,
4295ef3096fSSascha Wildner         &sc->irq_res_id, irq_flags);
4305ef3096fSSascha Wildner     if (!sc->irq_res)
43133190b70SSascha Wildner         return(FAILURE);
4325ef3096fSSascha Wildner     if (bus_setup_intr(sc->tws_dev, sc->irq_res, INTR_MPSAFE, tws_intr, sc,
4335ef3096fSSascha Wildner         &sc->intr_handle, NULL)) {
4345ef3096fSSascha Wildner             tws_log(sc, SETUP_INTR_RES);
43533190b70SSascha Wildner             return(FAILURE);
43633190b70SSascha Wildner     }
4375ef3096fSSascha Wildner     if (sc->intr_type == PCI_INTR_TYPE_MSI)
43833190b70SSascha Wildner             device_printf(sc->tws_dev, "Using MSI\n");
4395ef3096fSSascha Wildner     else
4405ef3096fSSascha Wildner             device_printf(sc->tws_dev, "Using legacy INTx\n");
44133190b70SSascha Wildner 
44233190b70SSascha Wildner     return(SUCCESS);
44333190b70SSascha Wildner }
44433190b70SSascha Wildner 
44533190b70SSascha Wildner static int
tws_init(struct tws_softc * sc)44633190b70SSascha Wildner tws_init(struct tws_softc *sc)
44733190b70SSascha Wildner {
44833190b70SSascha Wildner 
44933190b70SSascha Wildner     u_int32_t max_sg_elements;
45033190b70SSascha Wildner     u_int32_t dma_mem_size;
45133190b70SSascha Wildner     int error;
45233190b70SSascha Wildner     u_int32_t reg;
45333190b70SSascha Wildner 
45433190b70SSascha Wildner     sc->seq_id = 0;
45533190b70SSascha Wildner     if ( tws_queue_depth > TWS_MAX_REQS )
45633190b70SSascha Wildner         tws_queue_depth = TWS_MAX_REQS;
45733190b70SSascha Wildner     if (tws_queue_depth < TWS_RESERVED_REQS+1)
45833190b70SSascha Wildner         tws_queue_depth = TWS_RESERVED_REQS+1;
45933190b70SSascha Wildner     sc->is64bit = (sizeof(bus_addr_t) == 8) ? true : false;
46033190b70SSascha Wildner     max_sg_elements = (sc->is64bit && !tws_use_32bit_sgls) ?
46133190b70SSascha Wildner                                  TWS_MAX_64BIT_SG_ELEMENTS :
46233190b70SSascha Wildner                                  TWS_MAX_32BIT_SG_ELEMENTS;
46333190b70SSascha Wildner     dma_mem_size = (sizeof(struct tws_command_packet) * tws_queue_depth) +
46433190b70SSascha Wildner                              (TWS_SECTOR_SIZE) ;
46533190b70SSascha Wildner     if ( bus_dma_tag_create(NULL,                    /* parent */
46633190b70SSascha Wildner                             TWS_ALIGNMENT,           /* alignment */
46733190b70SSascha Wildner                             0,                       /* boundary */
46833190b70SSascha Wildner                             BUS_SPACE_MAXADDR_32BIT, /* lowaddr */
46933190b70SSascha Wildner                             BUS_SPACE_MAXADDR,       /* highaddr */
47033190b70SSascha Wildner                             BUS_SPACE_MAXSIZE,       /* maxsize */
47133190b70SSascha Wildner                             max_sg_elements,         /* numsegs */
47233190b70SSascha Wildner                             BUS_SPACE_MAXSIZE,       /* maxsegsize */
47333190b70SSascha Wildner                             0,                       /* flags */
47433190b70SSascha Wildner                             &sc->parent_tag          /* tag */
47533190b70SSascha Wildner                            )) {
47633190b70SSascha Wildner         TWS_TRACE_DEBUG(sc, "DMA parent tag Create fail", max_sg_elements,
47733190b70SSascha Wildner                                                     sc->is64bit);
47833190b70SSascha Wildner         return(ENOMEM);
47933190b70SSascha Wildner     }
48033190b70SSascha Wildner     /* In bound message frame requires 16byte alignment.
48133190b70SSascha Wildner      * Outbound MF's can live with 4byte alignment - for now just
48233190b70SSascha Wildner      * use 16 for both.
48333190b70SSascha Wildner      */
48433190b70SSascha Wildner     if ( bus_dma_tag_create(sc->parent_tag,       /* parent */
48533190b70SSascha Wildner                             TWS_IN_MF_ALIGNMENT,  /* alignment */
48633190b70SSascha Wildner                             0,                    /* boundary */
48733190b70SSascha Wildner                             BUS_SPACE_MAXADDR_32BIT, /* lowaddr */
48833190b70SSascha Wildner                             BUS_SPACE_MAXADDR,    /* highaddr */
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                            TWS_MAX_IO_SIZE,        /* maxsize */
52433190b70SSascha Wildner                            max_sg_elements,        /* nsegments */
52533190b70SSascha Wildner                            TWS_MAX_IO_SIZE,        /* maxsegsize */
52633190b70SSascha Wildner 			   BUS_DMA_ALLOCALL |      /* flags */
52733190b70SSascha Wildner 			   BUS_DMA_ALLOCNOW |
52833190b70SSascha Wildner                            BUS_DMA_PRIVBZONE |
52933190b70SSascha Wildner 			   BUS_DMA_PROTECTED,
53033190b70SSascha Wildner                            &sc->data_tag           /* tag */)) {
53133190b70SSascha Wildner         TWS_TRACE_DEBUG(sc, "DMA cmd tag Create fail", max_sg_elements, sc->is64bit);
53233190b70SSascha Wildner         return(ENOMEM);
53333190b70SSascha Wildner     }
53433190b70SSascha Wildner 
53533190b70SSascha Wildner     sc->reqs = kmalloc(sizeof(struct tws_request) * tws_queue_depth, M_TWS,
53633190b70SSascha Wildner                       M_WAITOK | M_ZERO);
53733190b70SSascha Wildner     sc->sense_bufs = kmalloc(sizeof(struct tws_sense) * tws_queue_depth, M_TWS,
53833190b70SSascha Wildner                       M_WAITOK | M_ZERO);
539*cec957e9SMatthew Dillon     sc->scan_ccb = xpt_alloc_ccb();
54033190b70SSascha Wildner 
54133190b70SSascha Wildner     if ( !tws_ctlr_ready(sc) )
54233190b70SSascha Wildner         if( !tws_ctlr_reset(sc) )
54333190b70SSascha Wildner             return(FAILURE);
54433190b70SSascha Wildner 
54533190b70SSascha Wildner     bzero(&sc->stats, sizeof(struct tws_stats));
54633190b70SSascha Wildner     tws_init_qs(sc);
54733190b70SSascha Wildner     tws_turn_off_interrupts(sc);
54833190b70SSascha Wildner 
54933190b70SSascha Wildner     /*
55033190b70SSascha Wildner      * enable pull mode by setting bit1 .
55133190b70SSascha Wildner      * setting bit0 to 1 will enable interrupt coalesing
55233190b70SSascha Wildner      * will revisit.
55333190b70SSascha Wildner      */
55433190b70SSascha Wildner 
55533190b70SSascha Wildner #ifdef TWS_PULL_MODE_ENABLE
55633190b70SSascha Wildner 
55733190b70SSascha Wildner     reg = tws_read_reg(sc, TWS_I2O0_CTL, 4);
55833190b70SSascha Wildner     TWS_TRACE_DEBUG(sc, "i20 ctl", reg, TWS_I2O0_CTL);
55933190b70SSascha Wildner     tws_write_reg(sc, TWS_I2O0_CTL, reg | TWS_BIT1, 4);
56033190b70SSascha Wildner 
56133190b70SSascha Wildner #endif
56233190b70SSascha Wildner 
56333190b70SSascha Wildner     TWS_TRACE_DEBUG(sc, "dma_mem_phys", sc->dma_mem_phys, TWS_I2O0_CTL);
56433190b70SSascha Wildner     if ( tws_init_reqs(sc, dma_mem_size) == FAILURE )
56533190b70SSascha Wildner         return(FAILURE);
56633190b70SSascha Wildner     if ( tws_init_aen_q(sc) == FAILURE )
56733190b70SSascha Wildner         return(FAILURE);
56833190b70SSascha Wildner 
56933190b70SSascha Wildner     return(SUCCESS);
57033190b70SSascha Wildner 
57133190b70SSascha Wildner }
57233190b70SSascha Wildner 
57333190b70SSascha Wildner static int
tws_init_aen_q(struct tws_softc * sc)57433190b70SSascha Wildner tws_init_aen_q(struct tws_softc *sc)
57533190b70SSascha Wildner {
57633190b70SSascha Wildner     sc->aen_q.head=0;
57733190b70SSascha Wildner     sc->aen_q.tail=0;
57833190b70SSascha Wildner     sc->aen_q.depth=256;
57933190b70SSascha Wildner     sc->aen_q.overflow=0;
58033190b70SSascha Wildner     sc->aen_q.q = kmalloc(sizeof(struct tws_event_packet)*sc->aen_q.depth,
58133190b70SSascha Wildner                               M_TWS, M_WAITOK | M_ZERO);
58233190b70SSascha Wildner     return(SUCCESS);
58333190b70SSascha Wildner }
58433190b70SSascha Wildner 
58533190b70SSascha Wildner static int
tws_init_trace_q(struct tws_softc * sc)58633190b70SSascha Wildner tws_init_trace_q(struct tws_softc *sc)
58733190b70SSascha Wildner {
58833190b70SSascha Wildner     sc->trace_q.head=0;
58933190b70SSascha Wildner     sc->trace_q.tail=0;
59033190b70SSascha Wildner     sc->trace_q.depth=256;
59133190b70SSascha Wildner     sc->trace_q.overflow=0;
59233190b70SSascha Wildner     sc->trace_q.q = kmalloc(sizeof(struct tws_trace_rec)*sc->trace_q.depth,
59333190b70SSascha Wildner                               M_TWS, M_WAITOK | M_ZERO);
59433190b70SSascha Wildner     return(SUCCESS);
59533190b70SSascha Wildner }
59633190b70SSascha Wildner 
59733190b70SSascha Wildner static int
tws_init_reqs(struct tws_softc * sc,u_int32_t dma_mem_size)59833190b70SSascha Wildner tws_init_reqs(struct tws_softc *sc, u_int32_t dma_mem_size)
59933190b70SSascha Wildner {
60033190b70SSascha Wildner 
60133190b70SSascha Wildner     struct tws_command_packet *cmd_buf;
60233190b70SSascha Wildner     cmd_buf = (struct tws_command_packet *)sc->dma_mem;
60333190b70SSascha Wildner     int i;
60433190b70SSascha Wildner 
60533190b70SSascha Wildner     bzero(cmd_buf, dma_mem_size);
60633190b70SSascha Wildner     TWS_TRACE_DEBUG(sc, "phy cmd", sc->dma_mem_phys, 0);
60733190b70SSascha Wildner     lockmgr(&sc->q_lock, LK_EXCLUSIVE);
60833190b70SSascha Wildner     for ( i=0; i< tws_queue_depth; i++)
60933190b70SSascha Wildner     {
61033190b70SSascha Wildner         if (bus_dmamap_create(sc->data_tag, 0, &sc->reqs[i].dma_map)) {
61133190b70SSascha Wildner             /* log a ENOMEM failure msg here */
6121dd5c540SSascha Wildner             lockmgr(&sc->q_lock, LK_RELEASE);
61333190b70SSascha Wildner             return(FAILURE);
61433190b70SSascha Wildner         }
61533190b70SSascha Wildner         sc->reqs[i].cmd_pkt =  &cmd_buf[i];
61633190b70SSascha Wildner 
61733190b70SSascha Wildner         sc->sense_bufs[i].hdr = &cmd_buf[i].hdr ;
61833190b70SSascha Wildner         sc->sense_bufs[i].hdr_pkt_phy = sc->dma_mem_phys +
61933190b70SSascha Wildner                               (i * sizeof(struct tws_command_packet));
62033190b70SSascha Wildner         sc->sense_bufs[i].posted = false;
62133190b70SSascha Wildner 
62233190b70SSascha Wildner         sc->reqs[i].cmd_pkt_phy = sc->dma_mem_phys +
62333190b70SSascha Wildner                               sizeof(struct tws_command_header) +
62433190b70SSascha Wildner                               (i * sizeof(struct tws_command_packet));
62533190b70SSascha Wildner         sc->reqs[i].request_id = i;
62633190b70SSascha Wildner         sc->reqs[i].sc = sc;
62733190b70SSascha Wildner 
62833190b70SSascha Wildner         sc->reqs[i].cmd_pkt->hdr.header_desc.size_header = 128;
62933190b70SSascha Wildner 
63033190b70SSascha Wildner         sc->reqs[i].state = TWS_REQ_STATE_FREE;
63133190b70SSascha Wildner         if ( i >= TWS_RESERVED_REQS )
63233190b70SSascha Wildner             tws_q_insert_tail(sc, &sc->reqs[i], TWS_FREE_Q);
63333190b70SSascha Wildner     }
63433190b70SSascha Wildner     lockmgr(&sc->q_lock, LK_RELEASE);
63533190b70SSascha Wildner     return(SUCCESS);
63633190b70SSascha Wildner }
63733190b70SSascha Wildner 
63833190b70SSascha Wildner static void
tws_dmamap_cmds_load_cbfn(void * arg,bus_dma_segment_t * segs,int nseg,int error)63933190b70SSascha Wildner tws_dmamap_cmds_load_cbfn(void *arg, bus_dma_segment_t *segs,
64033190b70SSascha Wildner                            int nseg, int error)
64133190b70SSascha Wildner {
64233190b70SSascha Wildner 
64333190b70SSascha Wildner     /* kprintf("command load done \n"); */
64433190b70SSascha Wildner 
64533190b70SSascha Wildner     *((bus_addr_t *)arg) = segs[0].ds_addr;
64633190b70SSascha Wildner }
64733190b70SSascha Wildner 
64833190b70SSascha Wildner void
tws_send_event(struct tws_softc * sc,u_int8_t event)64933190b70SSascha Wildner tws_send_event(struct tws_softc *sc, u_int8_t event)
65033190b70SSascha Wildner {
65133190b70SSascha Wildner     KKASSERT(lockstatus(&sc->gen_lock, curthread) != 0);
65233190b70SSascha Wildner     TWS_TRACE_DEBUG(sc, "received event ", 0, event);
65333190b70SSascha Wildner     switch (event) {
65433190b70SSascha Wildner 
65533190b70SSascha Wildner         case TWS_INIT_START:
65633190b70SSascha Wildner             sc->tws_state = TWS_INIT;
65733190b70SSascha Wildner             break;
65833190b70SSascha Wildner 
65933190b70SSascha Wildner         case TWS_INIT_COMPLETE:
66033190b70SSascha Wildner             KASSERT(sc->tws_state == TWS_INIT , ("invalid state transition"));
66133190b70SSascha Wildner             sc->tws_state = TWS_ONLINE;
66233190b70SSascha Wildner             break;
66333190b70SSascha Wildner 
66433190b70SSascha Wildner         case TWS_RESET_START:
66533190b70SSascha Wildner             /* multiple reset ? */
66633190b70SSascha Wildner             KASSERT(sc->tws_state != TWS_RESET, ("invalid state transition"));
66733190b70SSascha Wildner 
66833190b70SSascha Wildner             /* we can transition to reset state from any state */
66933190b70SSascha Wildner             sc->tws_prev_state = sc->tws_state;
67033190b70SSascha Wildner             sc->tws_state = TWS_RESET;
67133190b70SSascha Wildner             break;
67233190b70SSascha Wildner 
67333190b70SSascha Wildner         case TWS_RESET_COMPLETE:
67433190b70SSascha Wildner             KASSERT(sc->tws_state == TWS_RESET, ("invalid state transition"));
67533190b70SSascha Wildner             sc->tws_state = sc->tws_prev_state;
67633190b70SSascha Wildner             break;
67733190b70SSascha Wildner 
67833190b70SSascha Wildner         case TWS_SCAN_FAILURE:
67933190b70SSascha Wildner             KASSERT(sc->tws_state == TWS_ONLINE , ("invalid state transition"));
68033190b70SSascha Wildner             sc->tws_state = TWS_OFFLINE;
68133190b70SSascha Wildner             break;
68233190b70SSascha Wildner 
68333190b70SSascha Wildner         case TWS_UNINIT_START:
68433190b70SSascha Wildner             KASSERT(sc->tws_state == TWS_ONLINE || sc->tws_state == TWS_OFFLINE,
68533190b70SSascha Wildner                            ("invalid state transition"));
68633190b70SSascha Wildner             sc->tws_state = TWS_UNINIT;
68733190b70SSascha Wildner             break;
68833190b70SSascha Wildner     }
68933190b70SSascha Wildner 
69033190b70SSascha Wildner }
69133190b70SSascha Wildner 
69233190b70SSascha Wildner uint8_t
tws_get_state(struct tws_softc * sc)69333190b70SSascha Wildner tws_get_state(struct tws_softc *sc)
69433190b70SSascha Wildner {
69533190b70SSascha Wildner 
69633190b70SSascha Wildner     return((u_int8_t)sc->tws_state);
69733190b70SSascha Wildner 
69833190b70SSascha Wildner }
69933190b70SSascha Wildner 
70033190b70SSascha Wildner /* Called during system shutdown after sync. */
70133190b70SSascha Wildner 
70233190b70SSascha Wildner static int
tws_shutdown(device_t dev)70333190b70SSascha Wildner tws_shutdown(device_t dev)
70433190b70SSascha Wildner {
70533190b70SSascha Wildner 
70633190b70SSascha Wildner     struct tws_softc *sc = device_get_softc(dev);
70733190b70SSascha Wildner 
70833190b70SSascha Wildner     TWS_TRACE_DEBUG(sc, "entry", 0, 0);
70933190b70SSascha Wildner 
71033190b70SSascha Wildner     tws_turn_off_interrupts(sc);
71133190b70SSascha Wildner     tws_init_connect(sc, 1);
71233190b70SSascha Wildner 
71333190b70SSascha Wildner     return (0);
71433190b70SSascha Wildner }
71533190b70SSascha Wildner 
71633190b70SSascha Wildner /*
71733190b70SSascha Wildner  * Device suspend routine.
71833190b70SSascha Wildner  */
71933190b70SSascha Wildner static int
tws_suspend(device_t dev)72033190b70SSascha Wildner tws_suspend(device_t dev)
72133190b70SSascha Wildner {
72233190b70SSascha Wildner     struct tws_softc *sc = device_get_softc(dev);
72333190b70SSascha Wildner 
72433190b70SSascha Wildner     if ( sc )
72533190b70SSascha Wildner         TWS_TRACE_DEBUG(sc, "entry", 0, 0);
72633190b70SSascha Wildner     return (0);
72733190b70SSascha Wildner }
72833190b70SSascha Wildner 
72933190b70SSascha Wildner /*
73033190b70SSascha Wildner  * Device resume routine.
73133190b70SSascha Wildner  */
73233190b70SSascha Wildner static int
tws_resume(device_t dev)73333190b70SSascha Wildner tws_resume(device_t dev)
73433190b70SSascha Wildner {
73533190b70SSascha Wildner 
73633190b70SSascha Wildner     struct tws_softc *sc = device_get_softc(dev);
73733190b70SSascha Wildner 
73833190b70SSascha Wildner     if ( sc )
73933190b70SSascha Wildner         TWS_TRACE_DEBUG(sc, "entry", 0, 0);
74033190b70SSascha Wildner     return (0);
74133190b70SSascha Wildner }
74233190b70SSascha Wildner 
74333190b70SSascha Wildner 
74433190b70SSascha Wildner struct tws_request *
tws_get_request(struct tws_softc * sc,u_int16_t type)74533190b70SSascha Wildner tws_get_request(struct tws_softc *sc, u_int16_t type)
74633190b70SSascha Wildner {
74733190b70SSascha Wildner     struct tws_request *r = NULL;
74833190b70SSascha Wildner 
74933190b70SSascha Wildner     switch ( type ) {
75033190b70SSascha Wildner         case TWS_INTERNAL_CMD_REQ :
75133190b70SSascha Wildner             lockmgr(&sc->gen_lock, LK_EXCLUSIVE);
75233190b70SSascha Wildner             r = &sc->reqs[0];
75333190b70SSascha Wildner             if ( r->state != TWS_REQ_STATE_FREE ) {
75433190b70SSascha Wildner                 r = NULL;
75533190b70SSascha Wildner             } else {
75633190b70SSascha Wildner                 r->state = TWS_REQ_STATE_BUSY;
75733190b70SSascha Wildner             }
75833190b70SSascha Wildner             lockmgr(&sc->gen_lock, LK_RELEASE);
75933190b70SSascha Wildner             break;
76033190b70SSascha Wildner         case TWS_AEN_FETCH_REQ :
76133190b70SSascha Wildner             lockmgr(&sc->gen_lock, LK_EXCLUSIVE);
76233190b70SSascha Wildner             r = &sc->reqs[1];
76333190b70SSascha Wildner             if ( r->state != TWS_REQ_STATE_FREE ) {
76433190b70SSascha Wildner                 r = NULL;
76533190b70SSascha Wildner             } else {
76633190b70SSascha Wildner                 r->state = TWS_REQ_STATE_BUSY;
76733190b70SSascha Wildner             }
76833190b70SSascha Wildner             lockmgr(&sc->gen_lock, LK_RELEASE);
76933190b70SSascha Wildner             break;
77033190b70SSascha Wildner         case TWS_PASSTHRU_REQ :
77133190b70SSascha Wildner             lockmgr(&sc->gen_lock, LK_EXCLUSIVE);
77233190b70SSascha Wildner             r = &sc->reqs[2];
77333190b70SSascha Wildner             if ( r->state != TWS_REQ_STATE_FREE ) {
77433190b70SSascha Wildner                 r = NULL;
77533190b70SSascha Wildner             } else {
77633190b70SSascha Wildner                 r->state = TWS_REQ_STATE_BUSY;
77733190b70SSascha Wildner             }
77833190b70SSascha Wildner             lockmgr(&sc->gen_lock, LK_RELEASE);
77933190b70SSascha Wildner             break;
78033190b70SSascha Wildner         case TWS_GETSET_PARAM_REQ :
78133190b70SSascha Wildner             lockmgr(&sc->gen_lock, LK_EXCLUSIVE);
78233190b70SSascha Wildner             r = &sc->reqs[3];
78333190b70SSascha Wildner             if ( r->state != TWS_REQ_STATE_FREE ) {
78433190b70SSascha Wildner                 r = NULL;
78533190b70SSascha Wildner             } else {
78633190b70SSascha Wildner                 r->state = TWS_REQ_STATE_BUSY;
78733190b70SSascha Wildner             }
78833190b70SSascha Wildner             lockmgr(&sc->gen_lock, LK_RELEASE);
78933190b70SSascha Wildner             break;
79033190b70SSascha Wildner         case TWS_SCSI_IO_REQ :
79133190b70SSascha Wildner             lockmgr(&sc->q_lock, LK_EXCLUSIVE);
79233190b70SSascha Wildner             r = tws_q_remove_head(sc, TWS_FREE_Q);
79333190b70SSascha Wildner             if ( r )
79433190b70SSascha Wildner                 r->state = TWS_REQ_STATE_TRAN;
79533190b70SSascha Wildner             lockmgr(&sc->q_lock, LK_RELEASE);
79633190b70SSascha Wildner             break;
79733190b70SSascha Wildner         default :
79833190b70SSascha Wildner             TWS_TRACE_DEBUG(sc, "Unknown req type", 0, type);
79933190b70SSascha Wildner             r = NULL;
80033190b70SSascha Wildner 
80133190b70SSascha Wildner     }
80233190b70SSascha Wildner 
80333190b70SSascha Wildner     if ( r ) {
80433190b70SSascha Wildner         bzero(&r->cmd_pkt->cmd, sizeof(struct tws_command_apache));
80533190b70SSascha Wildner 	callout_init(&r->thandle);
80633190b70SSascha Wildner         r->data = NULL;
80733190b70SSascha Wildner         r->length = 0;
80833190b70SSascha Wildner         r->type = type;
80933190b70SSascha Wildner         r->flags = TWS_DIR_UNKNOWN;
81033190b70SSascha Wildner         r->error_code = TWS_REQ_ERR_INVALID;
81133190b70SSascha Wildner         r->ccb_ptr = NULL;
81233190b70SSascha Wildner         r->cb = NULL;
81333190b70SSascha Wildner         r->next = r->prev = NULL;
81433190b70SSascha Wildner     }
81533190b70SSascha Wildner     return(r);
81633190b70SSascha Wildner }
81733190b70SSascha Wildner 
81833190b70SSascha Wildner void
tws_release_request(struct tws_request * req)81933190b70SSascha Wildner tws_release_request(struct tws_request *req)
82033190b70SSascha Wildner {
82133190b70SSascha Wildner 
82233190b70SSascha Wildner     struct tws_softc *sc = req->sc;
82333190b70SSascha Wildner 
82433190b70SSascha Wildner     TWS_TRACE_DEBUG(sc, "entry", sc, 0);
82533190b70SSascha Wildner     lockmgr(&sc->q_lock, LK_EXCLUSIVE);
82633190b70SSascha Wildner     tws_q_insert_tail(sc, req, TWS_FREE_Q);
82733190b70SSascha Wildner     lockmgr(&sc->q_lock, LK_RELEASE);
82833190b70SSascha Wildner }
82933190b70SSascha Wildner 
83033190b70SSascha Wildner static device_method_t tws_methods[] = {
83133190b70SSascha Wildner     /* Device interface */
83233190b70SSascha Wildner     DEVMETHOD(device_probe,     tws_probe),
83333190b70SSascha Wildner     DEVMETHOD(device_attach,    tws_attach),
83433190b70SSascha Wildner     DEVMETHOD(device_detach,    tws_detach),
83533190b70SSascha Wildner     DEVMETHOD(device_shutdown,  tws_shutdown),
83633190b70SSascha Wildner     DEVMETHOD(device_suspend,   tws_suspend),
83733190b70SSascha Wildner     DEVMETHOD(device_resume,    tws_resume),
83833190b70SSascha Wildner 
83933190b70SSascha Wildner     DEVMETHOD(bus_print_child,      bus_generic_print_child),
84033190b70SSascha Wildner     DEVMETHOD(bus_driver_added,     bus_generic_driver_added),
841d3c9c58eSSascha Wildner     DEVMETHOD_END
84233190b70SSascha Wildner };
84333190b70SSascha Wildner 
84433190b70SSascha Wildner static driver_t tws_driver = {
84533190b70SSascha Wildner         "tws",
84633190b70SSascha Wildner         tws_methods,
84733190b70SSascha Wildner         sizeof(struct tws_softc)
84833190b70SSascha Wildner };
84933190b70SSascha Wildner 
85033190b70SSascha Wildner 
85133190b70SSascha Wildner static devclass_t tws_devclass;
85233190b70SSascha Wildner 
85333190b70SSascha Wildner /* DEFINE_CLASS_0(tws, tws_driver, tws_methods, sizeof(struct tws_softc)); */
854aa2b9d05SSascha Wildner DRIVER_MODULE(tws, pci, tws_driver, tws_devclass, NULL, NULL);
85533190b70SSascha Wildner MODULE_DEPEND(tws, cam, 1, 1, 1);
85633190b70SSascha Wildner MODULE_DEPEND(tws, pci, 1, 1, 1);
85733190b70SSascha Wildner 
85833190b70SSascha Wildner TUNABLE_INT("hw.tws.queue_depth", &tws_queue_depth);
8595ef3096fSSascha Wildner TUNABLE_INT("hw.tws.msi.enable", &tws_msi_enable);
860